概要
今天(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逆向。



2806

被折叠的 条评论
为什么被折叠?



