无敌免责声明:本案例用到的app
仅仅做为学习使用,切勿使用爬虫程序恶意攻击该服务器。
有了前面三节内容的铺垫,相信对抓包和模拟器配置都有了一些了解,这里实现一个完整的案例,仅做为入门学习记录。
APP
应用,明确抓取内容
接着向下翻页,滚动鼠标滚轮,之后回到顶部,点击第一个菜谱进入菜谱的详情页,回到fiddler
里面,设置停止抓包,在fiddler
左下角单机一下,变成空白,就是取消继续抓包
经过分析,发现数据都是已api.douguo.net
为host
的方式返回的,不同的数据,用了不同的URL
地址
上图fiddler
抓包分析之后,红色的请求就是爬虫程序所需要的,可以复制响应数据流到json.cn
里面粘贴查看。
分析完这些之后,再分析每一个请求的细节内容
经过三个链接的请求头对比之后,发现三个请求的请求头都是一样的,不同的是每一个请求携带的data
数据。
# 记录
# 豆果美食app案例
"""
在夜神模拟器中安装豆果美食apk,安装包可以在本地用浏览器搜索下载
打开fiddler工具,并修改Options的中的内容,
选项卡HTTPS from remote clients only
选项卡 Connections 端口8889 勾选Allow remote computers to connect(运行所有移动设备链接)
启动夜神模拟器的代理设置
"""
"""
fiddler已经能接收到数据包了,现在我们清空,走一遍app,让fiddler抓包
app走的流程是:首页-菜谱分类-蔬菜-土豆-菜谱-学做多-向下滑动鼠标(翻页操作)
抓包结束,在fiddler使用F12或鼠标点击左下角capture traffic停止抓包
接着来分析抓到的包:
观察发现,api.douguo.net是接口地址,重点查看返回的json数据
使用工具栏的find,在对话框中输入api.douguo.net,然后点击Find Sessions,现在所有域名和api.douguo.net相关的都变为黄色
然后查看这些包的数据,由于编码的问题,把返回数据放到浏览器json.cn里面查看
http://api.douguo.net/personalized/home HTTP/1.1 首页中部分类
http://api.douguo.net/recipe/flatcatalogs HTTP/1.1 点击“菜谱分类”后的所有分类
http://api.douguo.net/recipe/v2/search/0/20 HTTP/1.1 点击“学做多”后的内容
http://api.douguo.net/recipe/v2/search/20/20 HTTP/1.1 翻页操作
http://api.douguo.net/recipe/detail/957058 HTTP/1.1 详情页
"""
主程序:
import json
import requests
from multiprocessing import Queue
from APP数据抓取.spider_douguo import mogo
from concurrent.futures import ThreadPoolExecutor
# 创建队列
queue_list = Queue()
# 请求函数
def handler_request(url, data):
"""
:param url: 请求不同页面的链接
:param data: 请求不同页面时所附带的请求数据
:return: 获得的页面结果
"""
header = {
"client": "4",
"version": "6961.2",
"device": "OPPO R17",
"sdk": "22,5.1.1",
"channel": "baidu",
"resolution": "1280*720",
"display-resolution": "1280*720",
"dpi": "1.5",
# "android-id": "241c04e169bc5101",
# "pseudo-id": "4e169bc5101241c0",
"brand": "OPPO",
"scale": "1.5",
"timezone": "28800",
"language": "zh",
"cns": "0",
"carrier": "CHINA+MOBILE",
# "imsi": "460075101569188",
"User-Agent": "Mozilla/5.0 (Linux; Android 5.1.1; OPPO R17 Build/NMF26X; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36",
"uuid": "bb05a5b1-b4a8-4ae9-818a-8cf5e456a6ba",
"battery-level": "0.86",
"battery-state": "2",
"terms-accepted": "1",
"newbie": "1",
# "mac": "24:1C:04:E1:69:BC",
"imei": "866174518956913", # 这个不能去掉 ,这个是手机的ID,在模拟器里可查看
"reach": "10000",
"act-code": "1586589478",
"act-timestamp": "1586589478",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"Accept-Encoding": "gzip, deflate",
"Connection": "Keep-Alive",
# "Cookie": "duid=63925665",
"Host": "api.douguo.net",
# "Content-Length": "13",
}
# 在这里添加代理,当前代理链接已失效,只说明如何使用
# proxy = {"http": "http://H211EATS905745KC:[email protected]:9030"}
# response = requests.get(url=url, proxies=proxy)
response = requests.post(url=url, headers=header, data=data)
return response
# 分类菜谱
def handle_index():
"""
:return: 获取所有分类的名字,重组请求列表页的请求数据,并把数据放入队列,等待代用
"""
url = "http://api.douguo.net/recipe/flatcatalogs"
data = {"client": 4,
# "_session": 1586589481759866174518956913,
# "v": 1503650468,
"_vs": 2305,
# "sign_ran": "342696dee05600e1981d6cf95dfe22e9",
# "code": "22a8091f41d1c9bb"
}
response = handler_request(url=url, data=data)
# print(response.text)
index_response_dict = json.loads(response.text)
for index_item in index_response_dict["result"]["cs"]:
for index_item_sub in index_item["cs"]:
for item in index_item_sub["cs"]:
# print(item)
# 详情页client=4&_session=1586589481759866174518956913&keyword=%E5%9C%9F%E8%B1%86&order=3&_vs=11104&type=0&auto_play_mode=2&sign_ran=e31f5e1a08d5ea07b9e79a5d88f9a9df&code=a71450a7a3827c7a
# 解码后client=4&_session=1586589481759866174518956913&keyword=土豆&order=3&_vs=11104&type=0&auto_play_mode=2&sign_ran=e31f5e1a08d5ea07b9e79a5d88f9a9df&code=a71450a7a3827c7a
# 请求菜谱列表时要用到的请求数据
data_detail = {
"client": "4",
"_session": "1586589481759866174518956913",
"keyword": item["name"],
"order": "3",
"_vs": "11104",
"type": "0",
"auto_play_mode": "2",
# "sign_ran": "e31f5e1a08d5ea07b9e79a5d88f9a9df",
# "code": "a71450a7a3827c7a",
}
# print(data_detail)
queue_list.put(data_detail)
# 菜谱列表及详情页
def handle_caipu_list(data):
"""
:param data: 所有分类的名字
:return: 根据类名获取到分类列表,并第二次请求获取详情页数据,最后把组合的字典数据写入MongoDB数据库
"""
print("当前处理的食材:", data["keyword"])
caipu_list_url = "http://api.douguo.net/recipe/v2/search/0/20"
caipu_list_response = handler_request(url=caipu_list_url, data=data)
# print(caipu_list_response.text)
caipu_list_response_dict = json.loads(caipu_list_response.text)
for item in caipu_list_response_dict["result"]["list"]:
# print(item)
caipu_info = {}
caipu_info["shicai"] = data["keyword"]
if item["type"] == 13:
caipu_info["user_name"] = item["r"]["an"]
caipu_info["shicai_id"] = item["r"]["id"]
caipu_info["describe"] = str(item["r"]["cookstory"]).replace("\n", "").replace(" ", "")
caipu_info["caipu_name"] = item["r"]["n"]
caipu_info["zuoliao_list"] = item["r"]["major"]
caipu_info["pingfen"] = item["r"]["rate"]
caipu_info["people"] = item["r"]["recommendation_tag"]
# print(caipu_info)
detail_url = f"http://api.douguo.net/recipe/detail/{str(caipu_info['shicai_id'])}"
detail_data = {
"client": "4",
"_session": "1586589481759866174518956913",
"author_id": "0",
"_vs": "11101",
"is_new_user": "1",
# "sign_ran": "f98f1a6b40400f36fb07ec13242d5033",
# "code": "04765aa8dc22d71d"
}
detail_response = handler_request(url=detail_url, data=detail_data)
# print(detail_response.text)
detail_response_dict = json.loads(detail_response.text)
caipu_info["tips"] = detail_response_dict["result"]["recipe"]["tips"]
caipu_info["cook_step"] = detail_response_dict["result"]["recipe"]["cookstep"]
# print(json.dumps(caipu_info, ensure_ascii=False))
# Connect_mongo.insert_item(caipu_info)
mogo.mongo_info.insert_item(caipu_info)
else:
continue
if __name__ == '__main__':
handle_index()
pool = ThreadPoolExecutor(max_workers=20)
print(queue_list.qsize()) # 查看总共有多少数据
while queue_list.qsize() > 0:
pool.submit(handle_caipu_list, queue_list.get())
# 没有使用多进程的代码
# handle_caipu_list(queue_list.get())
# for _ in range(queue_list.qsize()):
# handle_caipu_list(queue_list.get())
如果有代理时,测试代理,这里仅用来参考,并没有使用代理
import requests
url = "http://ip.hahado.cn/ip" # 专门用来测试IP的一个链接
proxy = {"http": "http://H211EATS905745KC:[email protected]:9030"}
# proxy = {"ip": "180.126.44.136"}
response = requests.get(url=url, proxies=proxy)
# print(response.text) {"ip":"180.126.44.136","locale":""}
print(response.text)
MongoDB插入数据的代码
# 在Linux中使用yum安装MongoDB的安装
# /etc/init.d/mongod status 查看MongoDB运行的状态
# netstat -an | grep 27017 查看端口进程
# mongo exit退出
# liunx下载太慢,用win10
import pymongo
from pymongo.collection import Collection
# https://www.mongodb.com/download-center/community
class Connect_mongo(object):
def __init__(self):
self.client = pymongo.MongoClient(host="127.0.0.1", port=27017)
self.db = self.client["douguo"] # 自定义数据库名
# 插入数据的方法
def insert_item(self, item):
# 数据库名 自定义表名
db_collection = Collection(self.db, "douguo_item")
db_collection.insert_one(item)
mongo_info = Connect_mongo()
# mongo_info.insert_item({"aa": "bb"})
附:因为从fiddler
中复制的data
和请求头等数据都是原始的,然而在使用的时候要转换成字典格式
# 原始数据
client=4&_session=1586754944886866174518956913&author_id=0&_vs=11101&is_new_user=1&sign_ran=293eba6ee05e51218b100efbd1941db2&code=695e2bb1c2fb7d6f
# 处理之后的数据
"client":"4",
"_session":"1586589481759866174518956913",
"author_id":"0",
"_vs":"11101",
"is_new_user":"1",
"sign_ran":"f98f1a6b40400f36fb07ec13242d5033",
"code":"04765aa8dc22d71d",
# 下面二行是替换操作 用后面的字符替换前面的字符
# & \n 请求头不用替换换行
# = :
# 下面这一行是正则匹配替换
# (.*?):(.*) "$1":"$2",
步骤自行百度并操作安装配置,我用的是4.2
版本的,这个是下载地址https://www.mongodb.com/download-center/community
安装的时候,由于我的硬盘不是固态,最后会卡在进度条,一直等着就行了,我等了一个小时左右
至于配置,现在几乎不需要怎么配置就可以使用了,就连系统服务都是已经配置好了
最后运行爬虫,数据库保存数据如下
这个只是一个练手的入门小项目,没有涉及到其他的反爬手段。