每日一爬之中国观鸟记录中心-记录查询

概要

今天(2023/3/18)将教大家怎么对中国观鸟记录中心的记录查询进行爬取,难度为入门,内容包括:观察地点,记录用户,观察时间,鸟种数量等。

一、使用模块

import base64
import csv
import hashlib
import json
import random
import time

import requests
from Crypto.Cipher import PKCS1_v1_5, AES
from Crypto.PublicKey import RSA
from Crypto.Util.Padding import unpad

二、反爬技术

1.动态网页

动态网站通过JavaScript异步加载数据,需要查找数据接口,并且请求参数通常被加密参数保护。

2.数据加密

数据加密是为了保护数据的安全性和完整性,防止数据在传输过程中被恶意攻击者窃取或篡改。

3.请求头反爬

通过分析和验证请求头中的信息,网站可以识别出是否为正常用户的访问请求还是来自自动化工具的爬虫行为。

三、分析过程

1.打开中国观鸟记录中心 - 记录查询,空白处右键,打开开发者工具,选择网络选项卡,过滤Fetch/XHR请求,重新刷新网页,观察到有三个请求,其中一个响应内容为json格式并且数据量是Kb级

2.查看其响应内容,发现为数据对应的接口,但是核心数据被加密。

3.接下来,查看负截,发现同样有加密参数,

4.然后,查看请求标头, 发现存在两个类似加密的请求头参数,分别是Requestid,Sign。

 5.先进行请求头参数的逆向,以requestId作为关键词进行搜索,发现其他加密参数的加密过程也在这里

四、完整代码

import base64
import csv
import hashlib
import json
import random
import time

import requests
from Crypto.Cipher import PKCS1_v1_5, AES
from Crypto.PublicKey import RSA
from Crypto.Util.Padding import unpad


def get_uuid():
    hex_digits = "0123456789abcdef"
    s = [random.choice(hex_digits) for _ in range(32)]

    s[14] = '4'
    s[19] = hex_digits[(ord(s[19]) & 3) | 8]
    s[8] = s[13] = s[18] = s[23]

    return ''.join(s)


def enc_params(params):
    public_key = RSA.import_key(base64.b64decode(
        'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvxXa98E1uWXnBzXkS2yHUfnBM6n3PCwLdfIox03T91joBvjtoDqiQ5x3tTOfpHs3LtiqMMEafls6b0YWtgB1dse1W5m+FpeusVkCOkQxB4SZDH6tuerIknnmB/Hsq5wgEkIvO5Pff9biig6AyoAkdWpSek/1/B7zYIepYY0lxKQIDAQAB'))
    rsa = PKCS1_v1_5.new(public_key)
    enc_data = rsa.encrypt(params.encode('utf-8'))
    return base64.b64encode(enc_data).decode('utf-8')


def get_sign(params, request_id, timestamp):
    md5 = hashlib.md5()
    md5.update((params + request_id + timestamp).encode('utf-8'))
    return md5.hexdigest()


def dec_data(enc_data):
    key = "C8EB5514AF5ADDB94B2207B08C66601C".encode('utf-8')
    iv = '55DD79C6F04E1A67'.encode('utf-8')
    aes = AES.new(key, AES.MODE_CBC, iv=iv)

    dec_data = unpad(aes.decrypt(base64.b64decode(enc_data)), AES.block_size)
    return dec_data.decode('utf-8')


def extract_data(data):
    extracted_data = []
    for item in json.loads(data):
        extracted_data.append({
            'serialId': item['serialId'],
            'address': item['address'],
            'start_time': item['startTime'],
            'end_time': item['endTime'],
            'point_name': item['pointName'],
            'username': item['username'],
            'taxon_count': item['taxonCount'],
            'visits_count': item['visitsCount']
        })
    return extracted_data


def save_to_csv(data, filename='output.csv'):
    if not data:
        print("没有数据可保存。")
        return

    keys = data[0].keys()
    with open(filename, 'w', newline='', encoding='utf-8') as output_file:
        dict_writer = csv.DictWriter(output_file, fieldnames=keys)
        dict_writer.writeheader()
        dict_writer.writerows(data)


def main():
    timestamp = str(int(time.time() * 1000))
    total_pages = 5  # 设置要请求的总页面数
    all_extracted_data = []

    for page in range(1, total_pages + 1):
        params = '{"limit":"20","page":"%d"}' % page
        request_id = get_uuid()
        sign = get_sign(params, request_id, timestamp)

        headers = {
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,zh-TW;q=0.5',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Origin': 'https://www.birdreport.cn',
            'Pragma': 'no-cache',
            'Referer': 'https://www.birdreport.cn/',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0',
            'requestId': request_id,
            'sign': sign,
            'timestamp': timestamp,
        }

        try:
            data = enc_params(params)
            response = requests.post('https://api.birdreport.cn/front/activity/search', headers=headers, data=data)
            response.raise_for_status()  # Raise an error for bad responses
            enc_data = response.json().get('data', [])

            if enc_data:
                decrypted_data = dec_data(enc_data)
                extracted_data = extract_data(decrypted_data)
                all_extracted_data.extend(extracted_data)  # 累积数据
                print(f'页面 {page} 数据提取完成')
            else:
                print(f"页面 {page} 没有返回数据")
        except Exception as e:
            print(f"页面 {page} 发生错误: {e}")

    save_to_csv(all_extracted_data)
    print("所有数据已保存到output.csv")


if __name__ == "__main__":
    main()

小结

今天的内容比较简单,适合新手入门js逆向。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值