开发环境介绍
-----anaconda:
基于 数据分析和机器学习的集成环境(IDE)
-----jupyter:
anaconda提供的一个基于浏览器的可视化开发工具,可以把jupyter看成是anaconda的人性化版,就是为了方便开发者使用anaconda,如下图:
jupyter的基本使用
下载一个集成python的anaconda安装包即可,这里边内置了jupyter,输入jupyter notebook指令(安装anaconda时会自动键入环境变量)即可启动jupyter可视化的开发工具,相当于现在可以在127.0.0.1:8888这个网站里开发。然后再cd到xxx目录新建python3文件或者其他四种格式。
爬虫的三种分类:
1、通用爬虫:抓取整张页面源码。(一切爬虫的开始)
2、聚焦爬虫:抓取局部的数据(数据解析),聚焦爬虫是基于通用爬虫的。它才是我们的核心。
3、增量式爬虫:只爬最新更新的数据(或者说最新的url),这个肯定是要存一份上一次爬过的url来做对比的。
4、异步多任务爬虫:爬虫最头疼的就是网络请求,同步的概念是,发一个请求,就要等着远程服务器给响应,不给就一直等。而异步爬虫是,遇io等待则切换,用多线程或协程实现 ,用多进程浪费资源(计算类型,或没有io时间才用多进程)。
5、分布式爬虫:用scrapy-redis加redis来实现,多台机同时跑一摸一样的代码并且可以分担且不重复地(调度器去重)爬取海量url的数据。
补充:
像爬虫这种io密集型,多线程可以,协程也要的。但最好用协程(微线程,最轻量且最快),其实我们用python3.4后引入的asyncio内置模块就能实现异步协程抓取。后面会讲到,实现代码也不复杂。但后面有了Scrapy(异步抓取框架),即人家帮我们实现好了异步代码(基于多线程的),我们只需要使用框架就行了,有了asyncio和Scrapy两种方法实现了异步,就够快了,但有人还嫌不够快,他说再快也是一台机在跑,所以分布式就是多台机都跑异步(用scrapy框架或手写asyncio异步)代码,就更快了,前面是一台机抓1000个url,现在是多台机抓1000个url。
开篇肯定是讲爬虫的robots协议,下图是京东的robots协议,要是按照这个,我们啥也爬不了,所以道理谁都懂。
写爬虫,不要定死请求参数,尽量把参数动态化,不管是params(get请求),还是data(post请求或者说带请求体的请求)。
word = input("请输入您要搜索的内容:")
url = 'https://www.sogou.com/web'
params = {
'query':word,
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
}
html = requests.get(url=url,params=params,headers=headers).content.decode()
# 把请求来的页面源码写入到该文件
path = r'./{}.html'.format(word)
with open(path,'w',encoding='utf8') as f:
f.write(html)
动态数据的爬取:
爬虫的第一步必须是判断目标网站是否是ajax动态生成的数据,用浏览器抓包工具,浏览器栏请求的肯定是第一个数据包,定位并找到它的Response看看,如果全是js代码肯定是ajax动态生成,如果不确定就在Response里面搜页面的某个数据,搜不到就证明是动态加载的,再去全局搜,就能定位到ajax在后台偷偷请求的数据包,我们再模拟这个ajax请求,就能把动态加载的数据(这个数据包)拿到。
爬肯德基的餐厅信息:
http://www.kfc.com.cn/kfccda/storelist/index.aspx
通过下图的全局搜索,锁定我们页面的资源来源于后台ajax的post请求,请求的数据包是下图这个,请求地址为下图的这个url:
模拟这个ajax的post请求:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
}
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
data = {
"cname": "",
"pid": "",
"keyword": "北京",
"pageIndex": 1,
"pageSize": 10,
}
# data和params都是用来实现参数动态化的,如果是post请求,重心放在data。
json_str = requests.post(url=url,headers=headers,data=data).json()
for dict in json_str['Table1']:
# 根据动态数据包的respnse的数据结构,这样才能拿到想要的位置信息
position = dict['addressDetail']
print(position)
而这只是第一页的数据,如果想要获取所有页码的数据,就要去分析ajax请求的请求体参数了。既然知道是动态获取,那就直接开启抓包,点第二页,直接锁定XHR,请求的url肯定是定死的(ajax请求局部刷新),只是请求体的pageIndex不一样,所以我直接动态化index。
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
for pageIndex in range(1,3):
data = {
"cname": "",
"pid": "",
# 你甚至可以动态化keyword,全国每个省的每一页的门店信息
#"keyword": proc,
"keyword": "北京",
"pageIndex": pageIndex,
"pageSize": 10,
}
# data和params都是用来实现参数动态化的,如果是post请求,重心放在data。
json_str = requests.post(url=url,headers=headers,data=data).json()
# print(json_str)
for dict in str['Table1']:
position = dict['addressDetail']
print(position)
继续:爬取国家药监局每家企业的详情页的信息:
http://125.35.6.84:81/xk/
老规矩,进任意一个企业的详情页,查看该页面的数据书否是动态加载的数据。
找不到这条许可证数据,证明确实是前端ajax动态加载到页面上的,解决方法依然是全局搜索
得出结论:每一家企业的ajax请求地址是一样的,请求方式也是一样的,仅仅是data请求体那个唯一id不一样,结论:我们只需要拿到每个公司的这个加密id即可循环开爬
怎么找呢,肯定是去全局搜这个id,肯定能定位带一堆id串。
还真找到了。直接按照上图的参数和请求方式去拿到所有公司的id存到companyid_list列表中。
接着就动态化id,将这个id作为请求体,遍历id列表即可。
url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById'
for id in companyid_list:
data = {
"id":id,
}
json_str = requests.post(url=url,headers=headers,data=data).json()
businessPerson = json_str['businessPerson']
epsAddress = json_str['epsAddress']
print(businessPerson,epsAddress)
获取多页(前六页)企业id:
companyid_list = []
# 获取前6页的企业id
for page in range(1,7):
url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList'
data = {
"on":"true",
"page":page,
"pageSize":15,
"conditionType":1,
}
json_str = requests.post(url=url,headers=headers,data=data).json()
for dic in json_str["list"]:
id = dic['ID']
companyid_list.append(id)
print(companyid_list)
现在数据就会多很多,因为id列表增加成了前六页的企业id组成的列表:
爬图片方式:一是要二进制文件(content),二是要以二进制打开待存文件。
res = requests.get('https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2534506313,1688529724&fm=26&gp=0.jpg',headers=headers).content
with open(r'./123.png','wb') as f:
f.write(res)
梳理现在接触到的三种反爬机制:
robots协议,请求头UA检测,动态加载的数据。
如何判断爬取的页面数据是否是ajax请求动态生成的,基于抓包工具局部搜索(实际上一看response你就知道了,如果浏览器请求url对应返回的数据包全是或很多js代码只有很少的数据,肯定是动态生成的),是的话就在全局请求包中搜索页面上某个页面上的数据,定位到ajax请求回来的数据包,再模拟ajax请求拿动态数据。