手摸手带你App爬虫入门

你所耗费的时间,终将会有所回报,这一篇就是如此!

你好,我是悦创。

我将选取一个一款展示数据的 App 进行讲解,将会使用 Fiddler 抓包来分析数据请求的接口,以及使用 Python 编写爬虫逻辑,最后,把数据保存到 MongDB 里面去。

1. 准备工作

在跟随本教程开始之前,请确保你已经做完如下配置,以下是我本文的操作环境:

安装好 Python 3.6+,我这里使用的是 Python 3.7,并确保可以正常运行;

参考链接:https://www.aiyc.top/archives/527.html

Python 编译器 Sublime(如果有安装或者已经有 Pycharm 可以跳过准备工作);

参考链接:https://www.aiyc.top/archives/527.html

Python 爬虫常用库安装;

Python 通过 pip install xxx,来使得 Python 自动安装库。

这里就直接运行如下命令即可:pip3 install -r

安装好抓包工具:Fiddler;

Fiddler 安装:https://www.aiyc.top/archives/448.html

Fiddler 配置及简单操作:

配置 Fiddler 和手机代理:?p=11

夜神模拟器安装;

这里夜神模拟器安装较为简单,为了控制文章长度,如果不会安装的可以参考该链接:

安装 豆果美食 App,通过手机应用商店安装时,需要把代理关掉,等到需要在开启代理;

具体安装可以参考如下链接:,这里支持拖拽安装。

课程资料(包含 APP+代码):后台回复:douguo_Spider

2. 有可能的收获

对抓包工具 Fiddler 使用的入门和熟练使用;App 爬虫的抓取流程;MongoDB 菜谱数据入库操作;使用付费代理隐藏 IP;使用了队列技术;PS:因为时间关系,我并没有对代码进行如下部分优化:模块化延迟请求函数细分高内聚低耦合(较差)等等「如果你有对本代码进行优化和建议可以点击阅读原文在我博客下方留言和加小编好友,后台回复数字:3,即可添加,记得备注来意哦!」

3. 抓取豆果美食 App

3.1 目标

「分析豆果美食数据包」「通过 Python 多线程-线程池抓取数据」「通过使用代理 ip 隐藏爬虫」「将数据保存到 Mongodb 中」

打开 Fiddler、打开豆果美食,我们需要抓取豆果美食的菜谱分类,

我们可以操作时可以看见,你右边滚动(向下滚动/滑动),左边抓包的也在一直抓包,当然你可以自行选择抓取其他的数据。这里演示的只是:菜谱->蔬菜->土豆->学做最多;

接下来就是要分析抓取到的这些数据包,看 有没有我可以请求且所需要的数据接口,以及确认是否服务器会给我们相应的返回数据。

3.2 数据包分析

通过上面的操作我们可以获取的很多的数据包,接下来我们就来分析一下我们锁需要的数据包,找这些数据包其实有一些规律,比方说我们豆果美食,那它的数据肯定是以一个主站或者说主机头(主域名啥的),来进行请求的;

所以,我们可以 Finde 一下,也可以 Control + F,当然也可以在菜单栏:Edit->FindSessions,操作之后,界面如下:

Find:查找内容options:Search:设置查找(匹配范围)【Requests and response、Requests only、Response only、Url only】;Examine:检查;Match case:区分大小写;Regular Expression:正则表达式;Search binaries:搜索二进制文件;Decode compressed content:解码压缩内容;Search only selected sessions:只搜索选定的会话;Select matches:选择匹配;Unmark old results:取消标记旧的结果(比如第二次筛选,把上一次赛选的所标记的颜色取消掉);Result Highlight:结果强调(高亮);

我们可以在 api.douguo.net,如何我们就可以看见包含 api.douguo.net  的数据包都变成黄色。

然后,我点击去一个找,找到如下:

但是会发现这个数据包其实是,属于全部里面的数据包,并不是我们要选的学做最多的。

我接下来继续分析,看哪些是符合我们的数据包:

我在看看第二页,因为我们原先滚动(滑动了屏幕):

这样我们就可以知道,该 App 是每页20条菜谱数据。

注意:如果你上面找到对应的 json 数据,却显示貌似不正常,这个时候使用浏览器或者其他工具进行 json 格式化;

在线 Json 格式化网址:

效果如下:

原本:

格式化之后:

最终我们就找到的数据接口。

我把抓取到的数据导了出来,内容同学们可以点击该连接直接阅读:https://gitee.com/aiycgit/Study_Learning_DataBase/blob/master/douguo_App/90_Full.txt

3.3 编写爬虫代码

以下图片仅供参考:

3.3.1 构造请求头

编写 headers,这里我们把 fiddler 抓取到数据中的 headers 构造成字典,使用正则表达式:

(.*?):(.*)

"$1":"$2",

在 sublime 当中快捷键:Control + H,使用其他编辑器也是可以的,自行选择。

编写完成之后选 Replace All 即可:

"client":"4","version":"6962.2","device":"MI 5","sdk":"22,5.1.1","channel":"kp","resolution":"1920*1080","display-resolution":"1920*1080","dpi":"2.0","android-id":"1ff","pseudo-id":"9836f1f018","brand":"Xiaomi","scale":"2.0","timezone":"28800","language":"zh","cns":"2","carrier":"CMCC","imsi":"2499","User-Agent":"Mozilla/5.0 (Linux; Android 5.1.1; MI 5Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36","act-code":"","act-timestamp":"","uuid":"0c-a996-45c5-b351-f","battery-level":"0.97","battery-state":"3","mac":"F0:18:98:36:F9:97","imei":"2497","terms-accepted":"1","newbie":"0","reach":"10000","Content-Type":"application/x-www-form-urlencoded; charset=utf-8","Accept-Encoding":"gzip, deflate","Connection":"Keep-Alive","Cookie":"duid=","Host":"api.douguo.net","Content-Length":"179",

然后,就是要优化 headers 你要进行优化,那不是我们自己觉得哪些是可以有可无的是需要我们多抓取几次数据包之后得出来的结论。

当然,还有就是不断地测试,注释与取消注释,然后去请求看是否正常返回数据。

经过我地大量测试发现 imei 是不能去掉的,那这个 imei 是个什么东西呢?

可以打开我们夜神模拟器的设置,我们可以找到如下 imei 设置:

这个 imei 是随机生成的,我们需要进行保留的;

我们还可以看到 mac 地址,我们也可以进行注释掉,以及这两个 android-id、pseudo-id 我们也可以注释掉;

imsi 我们也把它注释掉,以及 Cookie 也注释掉,因为你一直带着这个 Cookie 去访问很容易被服务端检查并发现,如果这个 cookie 过时(失效了)这样也是访问不到的,还有就是 Content-Length 可以注释掉;这样一个 headers 就伪造完成了,以上注释是我测试过的,如果还有可以注释掉或者其他可以在下方留言交流。

3.3.2 构造全部栏目的 Data(index_data)

复制出来并构造成字典即可。

3.3.2 分析全部栏目&编写爬虫

然后我们需要编写请求,这里要注意,代理我们要在我们请求之后再加上去,也就是测试成功之后再往上加代理,不然你有可能搞不清楚 「是你代理除了问题还是请求数据的时候出现了问题,造成数据没有返回」。

为了抓取蔬菜中的全部数据,我要来分析一下主页的数据,那就算要分析主页的数据,所以也是要找到这个 API 接口的,

「为什么是这个呢?」

我们把它的 json 拿出来看看:

从上面看,我们可以得出结论,就是我们需要的主页数据。

接下来就是编写代码了,最终我们编写如下代码:

"""project = Code, file_name = Spider_douguo.py, author = AI悦创time = 2020/5/21 11:13, product_name = PyCharm, :AI悦创code is far away from bugs with the god animal protectingI love animals. They taste delicious."""# 导入请求库import jsonimport requestsfrom urllib import parsefrom requests.exceptions import RequestException# 分析请求的类型:post;# 分析变化的数据:url 变化;# post 的 data 是不一样的;# 分析不变的数据:headers 是不变的;HEADERS = { "client":"4", "version":"6962.2", "device":"MI 5", "sdk":"22,5.1.1", "channel":"kp", "resolution":"1920*1080", "display-resolution":"1920*1080", "dpi":"2.0", # "android-id":"1ff", # "pseudo-id":"9836f1f018", "brand":"Xiaomi", "scale":"2.0", "timezone":"28800", "language":"zh", "cns":"2", "carrier":"CMCC", # "imsi":"2499", "User-Agent":"Mozilla/5.0 (Linux; Android 5.1.1; MI 5Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36", "act-code":"", "act-timestamp":"", "uuid":"0c-a996-45c5-b351-f", "battery-level":"0.97", "battery-state":"3", # "mac":"F0:18:98:36:F9:97", "imei":"2497", "terms-accepted":"1", "newbie":"0", "reach":"10000", "Content-Type":"application/x-www-form-urlencoded; charset=utf-8", "Accept-Encoding":"gzip, deflate", "Connection":"Keep-Alive", # "Cookie":"duid=", "Host":"api.douguo.net", # "Content-Length":"179",}# print(parse.unquote(%E5%9C%9F%E8%B1%86))class DouGuo_Crawler: def __init__(self): self.headers = HEADERS # self.url = DETAIL_API_URL self.index_data = { "client": "4", # "_session": "15900281874962497", # "v": "",# 时间戳,也就是你请求数据的时间 "_vs": "2305", "sign_ran": "94b96c996a4ae435ee06ef133a", "code": "b366f93caa7bac04", } def request(self, url, data = None): try: response = requests.post(url = url, headers = self.headers, data = data) if response.status_code == 200: return response return None except RequestException: return None def main(self): url = html = self.request(url = url, data = self.index_data) print(html.text)if __name__ == __main__: douguo = DouGuo_Crawler().main()

运行结果:

{"state":"success","result":{"nv":"","cs":[{"name":"\u70ed\u95e8","id":"1","ju":"recipes:\/\/www.douguo.com\/search?key=\u70ed\u95e8&_vs=400","cs":[{"name":"\u5bb6\u5e38\u83dc","id":"18","ju":"recipes:\/\/www.douguo.com\/search?key=\u5bb6\u5e38\u83dc&_vs=400","cs":[{"name":"\u7ea2\u70e7\u8089","id":"5879","ju":"recipes:\/\/www.douguo.com\/search?key=\u7ea2\u70e7\u8089&_vs=400","cs":[],"image_url":""},{"name":"\u53ef\u4e50\u9e21\u7fc5","id":"5880","ju":"recipes:\/\/www.douguo.com\/search?key=\u53ef\u4e50\u9e21\u7fc5&_vs=400","cs":[],"image_url":""},{"name":"\u7cd6\u918b\u6392\u9aa8","id":"5881","ju":"recipes:\/\/www.douguo.com\/search?key=\u7cd6\u918b\u6392\u9aa8&_vs=400","cs":[],"image_url":""},{"name":"\u9c7c\u9999\u8089\u4e1d","id":"5882","ju":"recipes:\/\/www.douguo.com\/search?key=\u9c7c\u9999\u8089\u4e1d&_vs=400","cs":[],"image_url":""},{"name":"\u5bab\u4fdd\u9e21\u4e01","id":"5883","ju":"recipes:\/\/www.douguo.com\/search?key=\u5bab\u4fdd\u9e21\u4e01&_vs=400","cs":[],"image_url":""},{"name":"\u7ea2\u70e7\u6392\u9aa8","id":"5884","ju":"recipes:\/\/www.douguo.com\/search?key=\u7ea2\u70e7\u6392\u9aa8&_vs=400","cs":[],"image_url":""},{"name":"\u54b8","id":"478","ju":"recipes:\/\/www.douguo.com\/search?key=\u54b8&_vs=400","cs":[],"image_url":""},{"name":"\u81ed\u5473","id":"483","ju":"recipes:\/\/www.douguo.com\/search?key=\u81ed\u5473&_vs=400","cs":[],"image_url":""},{"name":"\u82e6","id":"479","ju":"recipes:\/\/www.douguo.com\/search?key=\u82e6&_vs=400","cs":[],"image_url":""},{"name":"\u9c9c","id":"5878","ju":"recipes:\/\/www.douguo.com\/user?id=&tab=1","cs":[],"image_url":""}],"image_url":""}],"image_url":""}],"ads":[{"dsp":{"id":"ad5671","pid":"96723","ch":1,"url":"","i":"","cap":"\u5e7f\u544a","position":"1recipecategory","query":"","client_ip":"","req_min_i":60,"channel":"","media_type":0,"request_count":1,"max_impression_count":0,"name":"\u840c\u840c\u54d2\u63a8\u5e7f\u5458","logo":"http:\/\/i1.douguo.net\/upload\/\/photo\/2\/1\/1\/70_a4a78ff0584e3f41d2de1a7191.jpg","user":{"nick":"\u840c\u840c\u54d2\u63a8\u5e7f\u5458","user_photo":"http:\/\/i1.douguo.net\/upload\/\/photo\/2\/1\/1\/70_a4a78ff0584e3f41d2de1a7191.jpg","lvl":7,"user_id":},"show":0,"ximage":"","canclose":0},"cid":"18"}]}}

输出结果过多,省略部分结果,然后把获取到的结果用 json 格式化数据即可观看具体的数据内容。

3.3.3 对全部栏目抓取到的 Json 进行解析

接下来,我就要抓取到的 json 进行数据解析,在解析之前我们需要进行数据的分析:我们可以看见全部的数据结果其实是在 result 里面的 cs 里面,如下图所示:

我们折叠起来的之后,可以知道一共有 14 个栏目,而对应的 Json 也是 14,如下图:

这也就是我们要遍历的数据,接下来我们再来看看里面的结构:

那目前就是:result->cs->cs->cs,所以我们目前的解析代码就可以写成如下:

import jsondef parse_flatcatalogs(self, html): """ :param html: Parse target text :return: """ json_content = json.loads(html)[result][cs] for index_items in json_content: for index_item in index_items[cs]: for item in index_item[cs]: print(item)

运行结果如下:

C:\Users\clela\AppData\Local\Programs\Python\Python37\python.exe "C:/Code/pycharm_daima/实战项目代码/豆果 App/Spider_douguo.py"{name: 红烧肉, id: 5915, ju: recipes://www.douguo.com/search?key=红烧肉&_vs=400, cs: [], image_url: }{name: 可乐鸡翅, id: 5916, ju: recipes://www.douguo.com/search?key=可乐鸡翅&_vs=400, cs: [], image_url: }{name: 糖醋排骨, id: 5917, ju: recipes://www.douguo.com/search?key=糖醋排骨&_vs=400, cs: [], image_url: }{name: 鱼香肉丝, id: 5918, ju: recipes://www.douguo.com/search?key=鱼香肉丝&_vs=400, cs: [], image_url: }{name: 宫保鸡丁, id: 5919, ju: recipes://www.douguo.com/search?key=宫保鸡丁&_vs=400, cs: [], image_url: }{name: 红烧排骨, id: 5920, ju: recipes://www.douguo.com/search?key=红烧排骨&_vs=400, cs: [], image_url: }{name: 怪味, id: 482, ju: recipes://www.douguo.com/search?key=怪味&_vs=400, cs: [], image_url: }{name: 苦, id: 479, ju: recipes://www.douguo.com/search?key=苦&_vs=400, cs: [], image_url: }{name: 鲜, id: 5878, ju: recipes://www.douguo.com/user?id=&tab=1, cs: [], image_url: }

输出结果过多,这里省略部分结果。

3.3.4 编写详情页的 Data

「我们接下来要如何操作呢?」

我们接下来不就是为了获取每个详情页嘛,不过我们还是以开头的土豆来写,我还是把抓取到的土豆的数据包复制出来分析:

client=4&_session=15901297891302497&keyword=%E5%9C%9F%E8%B1%86&order=0&_vs=11102&type=0&auto_play_mode=2&sign_ran=c7d28eb7687e6f13b0e0df1d8&code=9add6d8c1

这里,为什么只留 client 呢?其他的请求头其实前面已经分析并构造好了,而 url 也没什么好分析的,请求模式一看也就都知道 POST 请求,所以接下来我们来分析 client,我有两种方法对上面的 client 除了一个是 Python 代码的方法,如下:

from urllib import parsestr_txt = client=4&_session=15901297891302497&keyword=%E5%9C%9F%E8%B1%86&order=0&_vs=11102&type=0&auto_play_mode=2&sign_ran=c7d28eb7687e6f13b0e0df1d8&code=9add6d8c1result = parse.unquote(str_txt)print(result)

运行结果:

client=4&_session=15901297891302497&keyword=土豆&order=0&_vs=11102&type=0&auto_play_mode=2&sign_ran=c7d28eb7687e6f13b0e0df1d8&code=9add6d8c1

方法二使用 Fiddler 操作如下动图:

3.3.5 为什么不构造 url(api)呢?

「这里有同学会问了,为什么只看 client 而不可 url(api)呢?」

这是因为,这个 api 是豆果或者其他 App 也类似,你找到这个借口之后,你传入的 data 数据不一样,它给你放回的数据也是不一样的,具体操作同学们可以换一个栏目然后进行抓包,比如我这里选择了:水产海鲜->基围虾:

然后选择菜谱->学做多:

最终,我们滑动界面并抓包,抓包结果如下所示:

格式化都得结果:

这也就是为什么只看 client 的原因了,接下来我们上面的,我把解析之后的 client 构造成字典,上面已经演示过了,这里就不做过多的演示。

3.3.6 构造 data 成字典

构造结果如下:

"client":"4",# "_session":"15901297891302497","keyword":"土豆","order":"0","_vs":"11102","type":"0","auto_play_mode":"2","sign_ran":"c7d28eb7687e6f13b0e0df1d8","code":"9add6d8c1",

并编写如代码:

def __init__(self): self.headers = HEADERS # self.url = DETAIL_API_URL self.index_data = { "client": "4", # "_session": "15900281874962497", # "v": "",# 时间戳,也就是你请求数据的时间 "_vs": "2305", "sign_ran": "94b96c996a4ae435ee06ef133a", "code": "b366f93caa7bac04", } self.detail_data = { "client":"4", # "_session":"15901297891302497", "keyword":"土豆", "order":"0", "_vs":"11102", "type":"0", "auto_play_mode":"2", "sign_ran":"c7d28eb7687e6f13b0e0df1d8", "code":"9add6d8c1", }

我们需要修改 self.detail_data 修改 keyword,当然上面的 data 如果可以再次进行优化,欢迎你点击阅读原文留言给我鸭,之后会出抖音等 App 爬虫。

所以,修改成如下代码:

self.detail_data = { "client":"4", # "_session":"15901297891302497", "keyword": None, "order":"0", "_vs":"11102", "type":"0", "auto_play_mode":"2", "sign_ran":"c7d28eb7687e6f13b0e0df1d8", "code":"9add6d8c1", }

接下来也修改一下函数:「parse_flatcatalogs」

def parse_flatcatalogs(self, html): """ :param html: Parse target text :return: """ json_content = json.loads(html)[result][cs] for index_items in json_content: for index_item in index_items[cs]: for item in index_item[cs]: self.detail_data[keyword] = item[name] print(self.detail_data)

运行结果如下,结果过多,这里省略部分结果:

{client: 4, keyword: 红烧肉, order: 0, _vs: 11102, type: 0, auto_play_mode: 2, sign_ran: c7d28eb7687e6f13b0e0df1d8, code: 9add6d8c1}{client: 4, keyword: 可乐鸡翅, order: 0, _vs: 11102, type: 0, auto_play_mode: 2, sign_ran: c7d28eb7687e6f13b0e0df1d8, code: 9add6d8c1}{client: 4, keyword: 糖醋排骨, order: 0, _vs: 11102, type: 0, auto_play_mode: 2, sign_ran: c7d28eb7687e6f13b0e0df1d8, code: 9add6d8c1}{client: 4, keyword: 鱼香肉丝, order: 0, _vs: 11102, type: 0, auto_play_mode: 2, sign_ran: c7d28eb7687e6f13b0e0df1d8, code: 9add6d8c1}{client: 4, keyword: 宫保鸡丁, order: 0, _vs: 11102, type: 0, auto_play_mode: 2, sign_ran: c7d28eb7687e6f13b0e0df1d8, code: 9add6d8c1}3.3.7 构造队列&提交数据到队列

我们构造 data 成功了,接下来我们需要的是使用多线程,所以,我们就需要一个队列,所以这里就需要引入如下库:

from multiprocessing import Queue# 创建队列queue_list = Queue()

这样,我们的队列就创建成功了,那接下来我们就可以把我们的数据提交到我的队列当中去,也就是我们把我们的  self.detail_data 提交到,我们的队列当中,代码如下:queue_list.put(self.detail_data)

def parse_flatcatalogs(self, html): """ :param html: Parse target text :return: """ json_content = json.loads(html)[result][cs] for index_items in json_content: for index_item in index_items[cs]: for item in index_item[cs]: self.detail_data[keyword] = item[name] # print(self.detail_data) queue_list.put(self.detail_data)print(queue_list.qsize()) # 查看队列中有多少数据3.3.8 获取菜谱列表

前面我们获取到了各种食材,接下来就是要获取每一个食材的菜谱(做过最多),选取菜谱的 api,如下图:

recipe_api = # 先拿前 20 条数据进行测试

编写函数:

def the_recipe(self, data): """ 抓取菜谱数据 :return: """ print(f"当前出处理的食材: {data[keyword]}") recipe_api = # 先拿前 20 条数据进行测试 response = self.request(url = recipe_api, data = data).text # return response print(response)

修改 main 函数进行修改测试:

def main(self): # url = url = html = self.request(url = url, data = self.index_data).text # print(html) self.parse_flatcatalogs(html) # print(queue_list.get()) self.the_recipe(data = queue_list.get())

测试运行结果,结果过多,展示部分结果:

当前出处理的食材: 可乐鸡翅{"state":"success","result":{"sts":["\u53ef\u4e50\u9e21\u7fc5"],"hidden_sorting_tags":0,"list":[{"ju":"recipes:\/\/www.douguo.com\/details?id=","type":13,"r":{"stc":0,"sti":0,"an":"stta\u5c0f\u94ed","id":,"cookstory":"","n":"\u53ef\u4e50\u9e21\u7fc5[\u7b80\u5355\u5230\u6ca1\u4e0b\u8fc7\u53a8\u4e5f\u4f1a\u505a]","img":"https:\/\/cp1.douguo.com\/upload\/caiku\/8\/f\/c\/300_8f6d0f8b7195c0397c92fefc.jpg","dc":16948,"fc":34462,"ecs":1,"hq":0,"a":{"id":,"n":"stta\u5c0f\u94ed","v":1,"verified_image":"","progress_image":"","p":"http:\/\/tx1.douguo.net\/upload\/photo\/4\/0\/3\/70_u.jpg","lvl":6,"is_prime":false,"lv":0},"p":"https:\/\/cp1.douguo.com\/upload\/caiku\/8\/f\/c\/600_8f6d0f8b7195c0397c92fefc.jpg","p_gif":"https:\/\/cp1.douguo.com\/upload\/caiku\/8\/f\/c\/600_8f6d0f8b7195c0397c92fefc.jpg","cook_difficulty":"\u5207\u58a9(\u521d\u7ea7)","cook_time":"10~30\u5206\u949f","tags":[{"t":"\u665a\u9910"},{"t":"\u5348\u9910"},{"t":"\u7092\u9505"},{"t":"\u9505\u5177"},{"t":"\u70ed\u83dc"},{"t":"\u54b8\u751c"},

当然,我们可以把该数据进行 Json 格式化,如下结果:

我们把对应做的最多的,可乐鸡翅拿来对比一下:

表明数据抓取正常,这里我们可以继续点进去看详情介绍,因为第一条没有详情介绍,这里我点击第二天:

我们再打开第三个进行验证,不过我这里第三个又是没有介绍的:

所以只能点击第四个看有没有有了,结果第四个也没有,到第五个有了,对比如下:

所以,可以知道我们的请求是没有问题的,我们接下来继续操作,也就是进行所获取到的数据进行解析,所需的数据如同所示:

def the_recipe(self, data): """ 抓取菜谱数据 :return: """ print(f"当前出处理的食材: {data[keyword]}") recipe_api = # 先拿前 20 条数据进行测试 response = self.request(url = recipe_api, data = data).text # print(response) recipe_list = json.loads(response)[result][list] # print(type(recipe_list)) for item in recipe_list: print(item)

运行结果,数据较多,也是省略部分数据:

当前出处理的食材: 地三鲜https://cp1.douguo.com/upload/caiku/5/0/4/600_50d4ffc003fd4b43449cb0d8ed902174.jpg, p_gif: https://cp1.douguo.com/upload/caiku/5/0/4/600_50d4ffc003fd4b43449cb0d8ed902174.jpg, cook_difficulty: 切墩(初级), cook_time: 10-30分钟, tags: [{t: 东北菜}, {t: 炸}, {t: 炒}, {t: 咸}, {t: 下饭菜}, {t: 快手菜}, {t: 热菜}, {t: 素菜}, {t: 锅具}, {t: 炒锅}, {t: 午餐}, {t: 晚餐}, {t: 中国菜}], vc: , recommend_label: 1495人最近做过, display_ingredient: 1, major: [{note: 适量, title: 茄子}, {note: 一个, title: 土豆}, {note: 各一根, title: 青红椒}, {note: 适量, title: 葱姜蒜}], au: recipes://www.douguo.com/details?id=, pw: 799, ph: 450, rate: 4.7, recommendation_tag: 1495人做过}}{ju: recipes://www.douguo.com/details?id=, type: 13, r: {stc: 0, sti: 0, an: 咫尺光年-爱如初见, id: , cookstory: 这道地三鲜是我的拿手菜,想当初学这道菜的时候老公每天都试吃这菜,做了几次终于成功了。老公说再也不用吃不正宗的了,哈哈。这个味道就是外面饭店的味道。, n: 地三鲜~在家也能做出饭店的味道, img: https://cp1.douguo.com/upload/caiku/7/1/5/300_71c899c8795448414aaf426aa910a335.jpg, dc: 784, fc: 84217, ecs: 0, hq: 0, a: {id: , n: 咫尺光年-爱如初见, v: 1, verified_image: , progress_image: , p: http://tx1.douguo.net/upload/photo/1/0/e/70_u3574750219030220248.jpg, lvl: 7, is_prime: False, lv: 0}, p: https://cp1.douguo.com/upload/caiku/7/1/5/600_71c899c8795448414aaf426aa910a335.jpg, p_gif: https://cp1.douguo.com/upload/caiku/7/1/5/600_71c899c8795448414aaf426aa910a335.jpg, cook_difficulty: 切墩(初级), cook_time: 10-30分钟, tags: [{t: 咸鲜}, {t: 咸}, {t: 炒}, {t: 东北菜}, {t: 中国菜}], vc: , recommend_label: 784人最近做过, display_ingredient: 1, major: [{note: 一个, title: 长茄子}, {note: 一个, title: 土豆}, {note: 一个, title: 青椒}, {note: 1/3个, title: 彩椒}, {note: 若干, title: 蒜}, {note: 两颗, title: 葱}, {note: 适量, title: 花生油}, {note: 一勺, title: 生抽}, {note: 1/3勺, title: 红烧酱油}, {note: 两勺, title: 淀粉}, {note: 半勺, title: 醋}, {note: 一勺, title: 白糖}, {note: 适量, title: 盐}, {note: 半碗, title: 清水}], au: recipes://www.douguo.com/details?id=, pw: 800, ph: 800, rate: 4.8, recommendation_tag: 784人做过}}

不过,我们会发现有广告,那之后我们可以把广告去掉。

接下来,把数据返回类型为字典,我还能分析发现:type 为 13 的时候,就是我们所需要的菜谱,把需要的数据提取出来即可:

所以代码如下:

def the_recipe(self, data): """ 抓取菜谱数据 :return: """ print(f"当前出处理的食材: {data[keyword]}") recipe_api = # 先拿前 20 条数据进行测试 response = self.request(url = recipe_api, data = data).text # print(response) recipe_list = json.loads(response)[result][list] # print(type(recipe_list)) for item in recipe_list: # recipe_info : 菜谱信息 recipe_info = {} recipe_info[食材] = data[keyword] if item[type] == 13: recipe_info[user_name] = item[r][an] # 用户名称 recipe_info[recipe_id] = item[r][id] recipe_info[cookstory] = item[r][cookstory].strip() recipe_info[recipe_name] = item[r][n] recipe_info[major] = item[r][major] print(recipe_info) else: continue

不过要注意的是,上面提取的 ID 经过我前期的分析是食材的 ID,并不是用户的 id 而是菜谱的 id,那为什么要保留它呢?因为,后面我还需要请求一个 api 请求什么呢?

也就是我们目前抓取到的数据,里面并没有 「做法」 ,同学们自行对比哈。

「那这个做法在哪呢?」

也就是说,这里还有一个数据包,我们没有抓取到,待会我们再次请求做法的时候抓包看一下该做法的数据包时怎样的。所以,我们需要先把这个 id 留着。

3.3.9 抓包——recipe 烹饪方法

接下来,我们打开我们的 Fiddler 重新抓包,APP 里面直接停留在菜谱列表里面,直接进行抓包。最终我们抓取到了如下数据包:

表明这就是我们需要的数据包:

3.3.9.1 构造烹饪方法 url

所以我们接下来就来把这个请求编写成爬虫,我们把 API 复制出来:

上面的其实就是我们保存的上面保存的 id,然后构造 url:

detaile_cookstory_url = {id}.format(id = recipe_info[recipe_id])3.3.9.2 构造烹饪方法的 data

那接下来我们要构造一下 data 的数据,也是把它复制下来,并进行解码:

# 解码之前client=4&_session=15901913634092497&author_id=0&_vs=11101&_ext=%7B%22query%22%3A%7B%22kw%22%3A%22%E7%BA%A2%E7%83%A7%E8%82%89%22%2C%22src%22%3A%%22%2C%22idx%22%3A%221%22%2C%22type%22%3A%2213%22%2C%22id%22%3A%22%22%7D%7D&is_new_user=1&sign_ran=fa68eee8c3458c196bd65c15c4b06c3b&code=e5cf0cdca6a10f39# 解码之后client=4&_session=15901913634092497&author_id=0&_vs=11101&_ext={"query":{"kw":"红烧肉","src":"11101","idx":"1","type":"13","id":""}}&is_new_user=1&sign_ran=fa68eee8c3458c196bd65c15c4b06c3b&code=e5cf0cdca6a10f39

优化:

client=4&_session=15901913634092497&author_id=0&_vs=11101&_ext={"query":{"kw":"红烧肉","src":"11101","idx":"1","type":"13","id":""}}

也是构造成字典:

"client":"4","_session":"15901913634092497","author_id":"0","_vs":"11101","_ext":"{"query":{"kw":"红烧肉","src":"11101","idx":"1","type":"13","id":""}}",

我们还需要对上面的 "{"query":{"kw":"红烧肉","src":"11101","idx":"1","type":"13","id":""}}", 进优化,显示的不正常:

也就语法错误,把双引号换成单引号即可,修改成如下:

{"query":{"kw":"红烧肉","src":"11101","idx":"1","type":"13","id":""}}

最终我们的 cookstory_data 如下:

cookstory_data = { "client": "4", "_session": "15901913634092497", "author_id": "0", "_vs": "11101", "_ext": {"query":{"kw":"红烧肉","src":"11101","idx":"1","type":"13","id":""}}, }

而我们的 session 不需要我们可以注释掉,而我们的 id 、kw 是不断变化的,其他的我们不做变更,因为你通过几次抓包就会知道,它其实是一样的。这里为了满足懒惰的小白,我随机选择一道菜谱再次进行抓包。

3.3.9.3 对比 data 的不变的数据

我们再来看看它的数据:

# 解码之前:client=4&_session=15903617340342497&author_id=0&_vs=11101&_ext=%7B%22query%22%3A%7B%22kw%22%3A%22%E7%BA%B8%E6%9D%AF%E8%9B%8B%E7%B3%95%22%2C%22src%22%3A%%22%2C%22idx%22%3A%221%22%2C%22type%22%3A%2213%22%2C%22id%22%3A%%22%7D%7D&is_new_user=1&sign_ran=a866cbed9fd859fc2689fa0aaa&code=3ca60fe2ceb34663# 解码之后:client=4&_session=15903617340342497&author_id=0&_vs=11101&_ext={"query":{"kw":"纸杯蛋糕","src":"11101","idx":"1","type":"13","id":""}}&is_new_user=1&sign_ran=a866cbed9fd859fc2689fa0aaa&code=3ca60fe2ceb34663

我们来进行对比一下上面的红烧肉的数据:

# 红烧肉client=4&_session=15901913634092497&author_id=0&_vs=11101&_ext={"query":{"kw":"红烧肉","src":"11101","idx":"1","type":"13","id":""}}&is_new_user=1&sign_ran=fa68eee8c3458c196bd65c15c4b06c3b&code=e5cf0cdca6a10f39# 纸杯蛋糕client=4&_session=15903617340342497&author_id=0&_vs=11101&_ext={"query":{"kw":"纸杯蛋糕","src":"11101","idx":"1","type":"13","id":""}}&is_new_user=1&sign_ran=a866cbed9fd859fc2689fa0aaa&code=3ca60fe2ceb34663

可以,观察出确实是除了 kw、id 之外,src、type 等数据是不变的。

这个时候有同学可能会自己会在课下进行对比,发现 idx 这个数字有时候会不一样,我这里先做回答:

答:可能豆果美食升级后做了改动,抓包后,通过 python 去构造,注释掉一部分参数,也可以请求到数据,那么就不必去关心他变还是不变了,或者你在请求时,1是A结果,2是B结果。(也经过我的测试目前 idx 不用修改数据也是准确的。

❞3.3.9.4 编写代码def the_recipe(self, data): """ 抓取菜谱数据 :return: """ print(f"当前出处理的食材: {data[keyword]}") recipe_api = # 先拿前 20 条数据进行测试 response = self.request(url = recipe_api, data = data).text # print(response) recipe_list = json.loads(response)[result][list] # print(type(recipe_list)) for item in recipe_list: # recipe_info : 菜谱信息 recipe_info = {} recipe_info[食材] = data[keyword] if item[type] == 13: recipe_info[user_name] = item[r][an] # 用户名称 recipe_info[recipe_id] = item[r][id] recipe_info[cookstory] = item[r][cookstory].strip() recipe_info[recipe_name] = item[r][n] recipe_info[major] = item[r][major] # print(recipe_info) detaile_cookstory_url = {id}.format(id = recipe_info[recipe_id]) # print(detaile_cookstory_url) cookstory_data = { "client": "4", # "_session": "15901913634092497", "author_id": "0", "_vs": "11101", "_ext": {"query":{"kw":"%s","src":"11101","idx":"1","type":"13","id":"%s"}}%(str(data[keyword]), str(recipe_info[recipe_id])), } recipe_detail_resopnse = self.request(url = detaile_cookstory_url, data = cookstory_data).text # print(recipe_detail_resopnse) detaile_response_dict = json.loads(recipe_detail_resopnse) recipe_info[tips] = detaile_response_dict[result][recipe][tips] recipe_info[cook_step] = detaile_response_dict[result][recipe][cookstep] logger.info(f当前的 recipe_info:>>>{recipe_info}) # logger.info(当前入库的数据) else: continue

运行结果如下,省略部分结果:

当前出处理的食材: 可乐鸡翅2020-05-25 16:45:07,613 - __main__ - INFO - 当前的 recipe_info:>>>{食材: 可乐鸡翅, user_name: stta小铭, recipe_id: , cookstory: , recipe_name: 可乐鸡翅[简单到没下过厨也会做], major: [{note: 10个, title: 鸡翅}, {note: 一片, title: 姜}, {note: 两根, title: 葱}, {note: 一瓶易拉罐, title: 可乐}, {note: 三汤匙, title: 酱油(味极鲜)}], tips: 鸡翅用刀子或者叉子扎扎更容易入味,有功夫的话可以提前腌制,腌制有条件的话加几滴柠檬汁有加分哦。不用加盐巴,腌制可以用料酒和酱油,酱油生抽老抽无所谓,腌制20分钟以上就可以。\n--------------------------------------------------------\n常见问题问答:\n\n问:放不放盐巴?\n答:酱油里面含有盐分了。\n\n问:请问酱油是用生抽还是老抽?\n答:本文所用酱油为“味极鲜”\n\n问:鸡翅需要提前腌制吗?怎么腌?\n答:少许酱油和料酒腌制就可以了。\n\n问:我腌制的时候放了酱油了,煮的时候还要放吗?\n答:要!\n\n问:请问鸡翅需要焯水吗?\n答:如果您的鸡翅中间是白色,没有红色或者黑色血块,那可以不需要。反之则需要。\n\n问:不焯水不就很脏?\n答:请冲洗。\n\n问:请用问什么可乐好?\n答:便宜的那个。\n\n---------------------------------------------------------------------\n这道菜口味偏甜\n如果您是云贵川、或者西部的朋友,您可以加点辣椒(请勿加花椒)\n\n如果您是北方的朋友尤其是山东的朋友,200毫升的可乐请换成100毫升可乐+100毫升料酒\n---------------------------------------------------------------------\n感慨一下\n\n今天根据自己的菜谱做了一次,果然好简单,菜谱的做法也没问题。\n\n发布菜谱到现在,好不容易从料理小白,到厨师,到西餐主厨,日料主厨。真的喜欢做菜,有你们真好!有豆果真好!, cook_step: [{position: 1, content: 锅底放点油爆香姜和葱白, thumb: https://cp1.douguo.com/upload/caiku/f/6/1/140_f6ebfcafae6e4aa5b7f3e367f26b04b1.jpg, image_width: 640, image_height: 480, image: }, {position: 2, content: 放入鸡翅翻炒到发白,加入可乐和酱油,盖上锅盖, thumb: https://cp1.douguo.com/upload/caiku/d/a/4/140_da0d38fa2c885b8e829d17b1dcbbbd34.jpg, image_width: 640, image_height: 480, image: }, {position: 3, content: 中火煮到收汁就行, thumb: https://cp1.douguo.com/upload/caiku/1/b/3/140_1b122a5b97b867d9119caf2274e413c3.jpg, image_width: 640, image_height: 480, image: }]}2020-05-25 16:45:12,295 - __main__ - INFO - 当前的 recipe_info:>>>{食材: 可乐鸡翅, user_name: 葉子的爱与厨房, recipe_id: , cookstory: 这道菜太值得推荐了,别说孩子喜欢,老人也会喜欢吃。而且非常适合减肥人士,因为一滴油都不需要放喔。我的外甥女第一次吃这鸡翅的时候,破天荒的吃了7个鸡翅,吓的我妈妈一直在喊:不能吃了不能吃了。还有我那3岁半的干儿子,挑食挑的头疼,但是吃了这个,连汤汁都不放过,拌了一小碗米饭吃的干干净净。\n\n 所以,要是家里有个吃饭费劲的孩子,就动手试试这个吧。在寒冷的冬日,要是把它端上桌,不知有多合适了。还真别低估了它的魅力!这个真的比普通的可乐鸡翅好吃的多!, recipe_name: 可乐鸡翅(无油加姜版), major: [{note: 400g, title: 鸡中翅}, {note: 15g, title: 姜}, {note: 1罐, title: 可乐}, {note: 半个, title: 柠檬}, {note: 2勺, title: 生抽}, {note: 1勺, title: 老抽}, {note: 2g, title: 糖(可不放)}, {note: 2g, title: 盐}], tips: 1,鸡翅扎眼和切口都是为了让鸡翅更入味。\n2,用柠檬汁是为了去腥,也能更好的软化肉质。缩短煎的时间。并且增加口味。效果非常好 \n3,煎的时候正面先朝下。不过一定要不粘锅的锅喔。\n4,鸡翅会自己出很多油,所以千万不要再放油了。油是足够的。\n5,糖不要加多,因为可乐里糖分已经很重。怕甜的也可以不放糖 \n6,姜可以大胆多放些,因为可乐和姜的味道一融合就会让你受宠若惊。\n7,撇掉锅里的泡沫,除了用勺子。也可以准备一小张锡纸,用手捏皱,然后打开放进去注意下。拿出来泡沫就被吸在锡纸的褶皱里了,很简便的一种方法。\n8,中火可以多煮一下,这样可以更入味。然后大火收汁,可乐里含糖分,很快会收干的。\n9,汤汁拌点米饭,也特别好吃喔。, cook_step: [{position: 1, content: 把鸡中翅洗干净,沥水。或用厨房纸巾吸干水分,在鸡翅正面用叉子叉些眼(有鸡皮疙瘩的一面),在鸡翅背面用小刀横切2道小口 \n把姜切成姜丝3MM宽的姜丝。不要太细\n鸡翅放进一个大碗里,然后用手捏柠檬,尽量挤出柠檬汁,拌匀,放置3--5分钟, thumb: https://cp1.douguo.com/upload/caiku/b/f/b/140_bf8213a5b765c86f9acdbb6f09bc4f1b.jpg, image_width: 1600, image_height: 1200, image: }, {position: 2, content: 拿不粘锅的平底锅,加热后,直接放入鸡翅,有鸡皮疙瘩的一面先朝下, thumb: https://cp1.douguo.com/upload/caiku/9/1/2/140_91dca0da2408ee6a51e34600a36b9242.jpg, image_width: 800, image_height: 600, image: }, {position: 3, content: 出油后,翻面煎, thumb: https://cp1.douguo.com/upload/caiku/4/6/2/140_46b090e16b541d87597d893f14e021c2.jpg, image_width: 800, image_height: 600, image: }, {position: 4, content: 然后可以像我图中一样,尽量煎一下鸡翅的侧面,待双面鸡翅油差不多都出来,一直煎到两面金黄, thumb: https://cp1.douguo.com/upload/caiku/d/0/d/140_d0d236c86bd23dd50a099e1395397bbd.jpg, image_width: 800, image_height: 600, image: }, {position: 5, content: 放入切好的姜丝,和鸡翅一起翻炒一下,大约30秒, thumb: https://cp1.douguo.com/upload/caiku/2/9/7/140_297dadc823cf0fdd60fb93ac4c588e97.jpg, image_width: 800, image_height: 600, image: }, {position: 6, content: 倒入可乐,刚刚和鸡翅一样平或稍微没过鸡翅都可以。放盐,糖,生抽,老抽 。, thumb: https://cp1.douguo.com/upload/caiku/2/f/2/140_2f02fab255f69267708c2d37479d4922.jpg, image_width: 800, image_height: 600, image: }, {position: 7, content: 撇掉锅里的泡沫, thumb: https://cp1.douguo.com/upload/caiku/d/1/2/140_d11065f96e24b947ddcf6d0b4deb4752.jpg, image_width: 800, image_height: 600, image: }, {position: 8, content: 中火煮10分钟左右。然后大火收汁即可, thumb: https://cp1.douguo.com/upload/caiku/f/e/7/140_fe29798d4cd536b447f1aea295371c77.jpg, image_width: 670, image_height: 446, image: }]}2020-05-25 16:45:13,519 - __main__ - INFO - 当前的 recipe_info:>>>{食材: 可乐鸡翅, user_name: 拾光机, recipe_id: , cookstory: 良心保证,一个步骤做出超好吃可乐鸡翅!一个步骤就是——打开电饭锅,放进材料,按下煮饭键!是不是很简单!材料用量也很很好掌握,鸡翅按根来算,可乐买500毫升的小瓶装,连酱油、料酒和油都是用陶瓷汤匙来量取,我就不信还会有人做失败!, recipe_name: 电饭锅版可乐鸡翅, major: [{note: 1小瓶(500ML), title: 可乐}, {note: 8根, title: 鸡翅}, {note: 2汤匙, title: 酱油}, {note: 8片, title: 生姜}, {note: 1汤匙, title: 米酒或料酒}, {note: 1汤匙, title: 油}], tips: 不用加盐,酱油带咸味了。用普通酱油就行。, cook_step: [{position: 1, content: 鸡翅洗净,生姜切片。, thumb: https://cp1.douguo.com/upload/caiku/8/5/9/140_85dc807a52611577111a41f624c274f9.jpg, image_width: 2448, image_height: 3264, image: }, {position: 2, content: 在电饭锅里倒入1汤匙食用油,放进姜片抹匀油。, thumb: https://cp1.douguo.com/upload/caiku/2/9/e/140_2984b75726ab83d613a8ec9bdc47c64e.jpg, image_width: 2448, image_height: 3264, image: }, {position: 3, content: 放进鸡翅。姜片要放在鸡翅底下,去腥,防止粘锅。, thumb: https://cp1.douguo.com/upload/caiku/6/5/a/140_65581e3a3cf1c590e2456cc0f8be6c5a.jpg, image_width: 2448, image_height: 3264, image: }, {position: 4, content: 加入1小瓶(500ml)可乐,确保可乐没过鸡翅。, thumb: https://cp1.douguo.com/upload/caiku/4/d/6/140_4dbfef984db6d25341b969f92d3136d6.jpg, image_width: 2448, image_height: 3264, image: }, {position: 5, content: 加2汤匙酱油,1汤匙料酒。, thumb: https://cp1.douguo.com/upload/caiku/1/5/e/140_156df43cd12523824965719c22d387be.jpg, image_width: 2448, image_height: 3264, image: }, {position: 6, content: 按下煮饭键。, thumb: https://cp1.douguo.com/upload/caiku/3/0/a/140_302ee95370e83cecd8b0d93b797e07ca.jpg, image_width: 2448, image_height: 3264, image: }, {position: 7, content: 等电饭锅跳到保温档就好啦。, thumb: https://cp1.douguo.com/upload/caiku/8/4/5/140_847f877dddb7859e7f0d2d221f923095.jpg, image_width: 2448, image_height: 3264, image: }, {position: 8, content: 对了,喜欢吃辣的可以加干辣椒,加几粒大蒜味道也不错。, thumb: https://cp1.douguo.com/upload/caiku/d/2/3/140_d20df3ba8675ff5339a0b2af547574b3.jpg, image_width: 3456, image_height: 5184, image: }]}

我们可以把输出的数据转换成 json 方便阅读,代码如下:

json_recipe_iinfo = json.dumps(recipe_info)logger.info(f当前的 recipe_info:>>>{json_recipe_iinfo})3.3.10 数据入库逻辑编写

那到现在,我们还需要进行数据的入库,ip 隐藏,当然还有多线程我们还没有实现。这里我们需要导入这样的一个库:

import pymongo

不过这里,我们会选择把存储数据的代码放入到单独的一个代码文件,这里我在项目目录下创建一个 db.py 的文件。

如果,你对 Python 操作 MongoDB 不了解的话,可以参考如下链接:

数据库代码如下:

"""project = Code, file_name = db.py, author = AI悦创time = 2020/5/26 7:25, product_name = PyCharm, :AI悦创code is far away from bugs with the god animal protectingI love animals. They taste delicious."""import pymongofrom pymongo.collection import Collectionclass Connect_mongo(object): def __init__(self): self.client = pymongo.MongoClient(host=localhost, port=27017) self.db = self.client[dou_guo_meishi_app] # 指定数据库 def insert_item(self, item): db_collection = Collection(self.db, dou_guo_mei_shi_item) # 创建表(集合) # db_collection = self.db.dou_guo_mei_shi_item db_collection.insert_one(item) mongo_info = Connect_mongo()

我们还在 Spider_douguo.py 添加了如下代码:

from db import mongo_infodef the_recipe(self, data): """ 抓取菜谱数据 :return: """ # print(f"当前出处理的食材: {data[keyword]}") recipe_api = # 先拿前 20 条数据进行测试 response = self.request(url = recipe_api, data = data).text # print(response) recipe_list = json.loads(response)[result][list] # print(type(recipe_list)) for item in recipe_list: # recipe_info : 菜谱信息 recipe_info = {} recipe_info[食材] = data[keyword] if item[type] == 13: recipe_info[user_name] = item[r][an] # 用户名称 recipe_info[recipe_id] = item[r][id] recipe_info[cookstory] = item[r][cookstory].strip() recipe_info[recipe_name] = item[r][n] recipe_info[major] = item[r][major] # print(recipe_info) detaile_cookstory_url = {id}.format(id = recipe_info[recipe_id]) # print(detaile_cookstory_url) cookstory_data = { "client": "4", # "_session": "15901913634092497", "author_id": "0", "_vs": "11101", "_ext": {"query":{"kw":"%s","src":"11101","idx":"1","type":"13","id":"%s"}}%(str(data[keyword]), str(recipe_info[recipe_id])), } recipe_detail_resopnse = self.request(url = detaile_cookstory_url, data = cookstory_data).text # print(recipe_detail_resopnse) detaile_response_dict = json.loads(recipe_detail_resopnse) recipe_info[tips] = detaile_response_dict[result][recipe][tips] recipe_info[cook_step] = detaile_response_dict[result][recipe][cookstep] # json_recipe_iinfo = json.dumps(recipe_info) # logger.info(f当前的 recipe_info:>>>{json_recipe_iinfo}) logger.info(f"当前入库的菜谱是:>>>{recipe_info[recipe_name]}") mongo_info.insert_item(recipe_info) else: continue

运行代码之后,我们可以查看数据库中的数据:

3.3.11 多线程逻辑编写

这里我只是演示了前20条菜谱,同学们可以自行构造 url,接下来我们就可以编写多线程,这里我们需要导入如下库:

from concurrent.futures import ThreadPoolExecutor

main 函数的代码如下:

def main(self): # url = url = html = self.request(url = url, data = self.index_data).text # print(html) self.parse_flatcatalogs(html) # print(queue_list.qsize()) # for _ in range(queue_list.qsize()): # data = queue_list.get() with ThreadPoolExecutor(max_workers=20) as thread: while queue_list.qsize() > 0: thread.submit(self.the_recipe, queue_list.get())

运行之后,就可以看到抓取到的数据:

3.3.12 IP 隐藏

IP 隐藏的目的以及作用,我这里就不做赘述了。这里,我创建一个测试文件,来测试我们的代理是否有效,这里我使用的是 【阿布云代理】: ,同学们可以自行选择,如果对代理的使用不清楚的话,可以点击此链接阅读:Requests 和 Scrapy 中的代理 IP 设置

▌使用付费代理

上面,我们只使用了一个代理,而在爬虫中往往需要使用多个代理,那有如何构造呢,这里主要两种方法:

一种是使用免费的多个 IP;一种是使用付费的 IP 代理;

免费的 IP 往往效果不好,那么可以搭建 IP 代理池,但对新手来说搞一个 IP 代理池成本太高,如果只是个人平时玩玩爬虫,完全可以考虑付费 IP,几块钱买个几小时动态 IP,多数情况下都足够爬一个网站了。

这里推荐一个付费代理「阿布云代理」,效果好也不贵,如果你不想费劲地去搞 IP 代理池,那不妨花几块钱轻松解决。

img

首次使用的话,可以选择购买一个小时的动态版试用下,点击生成隧道代理信息作为凭证加入到代码中。

imgimgimg

将信息复制到官方提供的 Requests 代码中,运行来查看一下代理 IP 的效果:

http-proxy/dyn-manual-python.html

import requests# 待测试目标网页targetUrl = ""def get_proxies():# 代理服务器proxyHost = "http-dyn.abuyun.com"proxyPort = "9020"# 代理隧道验证信息proxyUser = "HSW5CD"proxyPass = "CBE9D1D21DC94189"proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % {"host" : proxyHost,"port" : proxyPort,"user" : proxyUser,"pass" : proxyPass,}proxies = {"http": proxyMeta,"https" : proxyMeta,}for i in range(1,6):resp = requests.get(targetUrl, proxies=proxies)# print(resp.status_code)print(第%s次请求的IP为:%s%(i,resp.text))get_proxies()

可以看到每次请求都会使用不同的 IP,是不是很简单?比搞 IP 代理池省事多了。

第1次请求的IP为:125.117.134.158第2次请求的IP为:49.71.117.45第3次请求的IP为:112.244.117.94第4次请求的IP为:122.239.164.35第5次请求的IP为:125.106.147.24[Finished in 2.8s]

当然,还可以这样设置:

"""project = Code, file_name = lession, author = AI悦创time = 2020/5/26 10:36, product_name = PyCharm, :AI悦创code is far away from bugs with the god animal protectingI love animals. They taste delicious."""import requests# 112.48.28.233url = # 下面的 try:......except:......是一个防错机制# username:password@代理服务器ip地址:portproxy = {http:http://HSW5CD:[email protected]:9020}try:response = requests.get(url, proxies = proxy)print(response.status_code)if response.status_code == 200:print(response.text)except requests.ConnectionError as e:# 如果报错,则输出报错信息print(e.args)

以上,介绍了 Requests 中设置代理 IP 的方法。

▌没用代理之前import requestsurl = # 下面的 try:......except:......是一个防错机制try: response = requests.get(url) #不使用代理 print(response.status_code) if response.status_code == 200: print(response.text)except requests.ConnectionError as e: # 如果报错,则输出报错信息 print(e.args)

输出结果:

200112.48.28.233[Finished in 1.0s]

▌使用代理

import requests# 112.48.28.233url = # 下面的 try:......except:......是一个防错机制# username:password@代理服务器ip地址:portproxy = {http:http://HSW5CD:[email protected]:9020}try: response = requests.get(url, proxies = proxy) print(response.status_code) if response.status_code == 200: print(response.text)except requests.ConnectionError as e: # 如果报错,则输出报错信息 print(e.args)

输出结果:

200221.227.240.111

这样我们就成功的使用代理了,接下来我们写入爬虫代码。

▌代理写入

因为,我买的每秒只能请求五次,所以我的线程池也要改成5,不然会报错。(有钱人随意购买或者考虑赞赏小编,下一篇抖音)

我 max_workers 我改成了 2。

如果,觉得本篇对你有帮助欢迎转发给你的小伙伴们,也可以考虑赞赏嘿嘿,在看走起,skr~。

对于本文内容有不理解的可以点击阅读原文在原文留言,还有就是加入交流群交流。

长按识别下方二维码,和众多位岛民一起

把别人的顿悟,变成你的基本功

 花半秒钟就看透事物本质的人,  和花一辈子都看不清的人,  注定是截然不同的命运。