内容为【2020年Python爬虫全套课程(学完可做项目)】视频学习笔记
基本概念
- 什么是爬虫
爬虫是通过编写程序,模拟浏览器上网,然后让其去互联网上抓取数据的过程。 - 爬虫的价值
抓取互联网上的数据,再讲爬取的数据为我所用,产业化、商业化。 - 爬虫是否违法
(1)在法律中是不被禁止的。
(2)具有违法风险。 - 违法爬虫行为
(1)爬虫干扰了被访问网站的正常运营。
(2)爬虫抓取了受到法律保护的特定类型的数据或信息。 - 如何避免违法爬虫
(1)优化程序,避免干扰被访问网站的正常运营。
(2)在使用和传播爬取到的数据时,审查所抓取的内容,一旦涉及敏感内容立即停止。 - 使用场景
(1)通用爬虫:搜索引擎抓取系统的重要组成部分,抓取的是互联网中一整张页面的数据。
(2)聚焦爬虫:建立在通用爬虫的基础上,抓取的是页面中特定的局部内容。
(3)增量式爬虫:检测网站中数据更新的情况,只会抓取网站中最新更新的数据。 - 反爬机制
门户网站可以通过指定相应的策略或者技术手段,防止爬虫程序进行网站数据的爬虫。 - 反反爬策略
爬虫程序可以通过指定相关的从策略或者技术手段,破解门户网站中具备的反爬机制,从而获取门户网站中相关的数据。 - robots.txt协议
规定了网站中哪些数据可以被爬虫爬取,哪些数据不可以被爬取。robots协议并不是一个规范,而只是约定俗成的,所以并不能保证网站的隐私。
查看某个网站的robots协议只需在域名后加“/robots.txt”。 - http协议
服务器和客户端进行数据交互的一种形式。(超文本传输协议) - 常用请求头信息
(1)User-Agent:表示请求载体的身份标识。(使用代码进行请求默认是代码的身份标识)
【在浏览器开发者工具–》Network–》选择一个包打开–》查看Headers(头信息)–》Request Headers(请求头信息)–》User-Agent】
(2)Connection:请求完毕后,是断开连接还是保持连接 - 响应头信息
Content-Type:服务器响应回客户端的数据类型。 - https协议
安全的http协议,数据传输和交互过程中数据加密。(采用证书秘钥加密) - 加密方式
(1)对称秘钥加密
(2)非对称秘钥加密(公开秘钥加密)
(3)证书秘钥加密
【HTTPS中的对称密钥加密,公开密钥加密,数字证书】
requests模块
涉及网络请求的模块还有一个urllib模块,比较古老,使用不方便。
requests模块是python中原生的一款基于网络请求的模块,功能非常强大,使用简单便捷、效率高。
这两个模块的作用都是模拟浏览器发请求。
- 使用步骤:
(1)指定url
(2)发起请求
(3)获取响应数据
(4)持久化存储 - 环境安装
pip install requests
实例:爬取搜狗首页的页面数据
import requests
if __name__ == "__main__":
# step 1: 指定url
url = 'https://www.sogou.com'
# step 2: 发起请求,get方法会返回一个响应对象
response = requests.get(url=url)
# step 3: 获取响应数据,text返回的是字符串形式的响应数据
page_text = response.text
print(page_text)
# step 4: 持久化存储(将爬取内容保存到根目录)
with open('./sogou.html','w',encoding='utf-8') as fp:
fp.write(page_text)
实例:爬取搜狗指定词条对应的搜索结果页面(简易网页采集器)
import requests
if __name__ == "__main__":
# step 1: 指定url
url = 'https://www.sogou.com/web?'
# 处理url携带的参数:封装到字典中
kw = input('enter a word:')
param = {
'query':kw
}
# 伪装成由浏览器,防止爬取页面乱码
headers = {
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)'
}
# 对指定的url发起的请求对应的url是携带参数的,并且请求过程中处理了参数
response = requests.get(url=url, params=param, headers=headers)
page_text = response.text
fileName = kw+'.html'
with open(fileName, 'w', encoding='utf-8') as fp:
fp.write(page_text)
- UA伪装——反爬机制
UA:User-Agent,请求载体的身份标识。当使用流量器对对应网址发起请求时,当前url所对应的请求载体就是所使用的浏览器。爬虫程序使用requests的get方法也可以发起请求,此时请求载体的身份标识就不是浏览器了,而是爬虫程序。
UA检测:门户网站的服务器会检测对应请求的载体身份标识,如果检测到请求的载体身份标识为某一款浏览器,则判断当前请求是一个正常的请求,服务器端对请求行为放行。但是当服务器端检测到请求载体的身份标识不是基于某一款浏览器的,则判断当前请求是一个非正常请求(爬虫),服务器端对请求行为拒绝。
UA伪装:让爬虫对应的请求载体身份标识伪装成某一款浏览器,完成爬虫程序的正常请求。将对应的User-Agent封装到字典中。
实例:破解百度翻译
在不使用数据解析的情况下拿到百度翻译的翻译结果。
观察百度翻译运行步骤:
(1)打开百度翻译页面
(2)打开开发者工具–》点击Network–》定位到XHR(All对应的是所有的请求数据包,XHR对应的是Ajax对应的请求数据包)
(3)输入单词“cat”后,挨个点击Ajax对应的请求数据包,寻找完整单词对应的Ajax请求数据包(Request Method: POST,query: cat)。Response Headers中Content-Type表示服务器端响应回客户端的数据类型(content-type: application/json,是一个json串)
(4)点击Response选项卡,查看Response选项卡中存储的是不是服务器端响应的数据
(5)结论1:翻译行为对应的请求是一个post请求,并且该post请求携带了参数,需要使用requests模块对post请求的url进行post请求的发送,并且在发送过程中处理参数。问题1:如何使用requests模块发起post请求;问题2:post请求发送后如何处理post请求所携带的参数。
(6)结论2:响应数据是一组json数据,这组json数据是单词的翻译结果,即需要被捕获的数据。
import requests
import json
if __name__ == "__main__":
post_url = 'https://fanyi.baidu.com/sug'
word = input('enter a word:')
data = {
'kw':word
}
headers = {
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)'
}
response = requests.post(url=post_url, data=data, headers=headers)
# 获取响应数据,response.txt获取是一串不能直接被人读取的字符串。
# json()方法返回obj(这里是一个字典对象),但是只有当确认响应数据为json类型时才能使用json()方法进行对象的返回。
# 从content-type中查看返回的数据类型
dic_obj = response.json()
print(dic_obj)
# 持久化存储,将字典对象直接存入json格式的文本文件中
fp = open('./cat.json', 'w', encoding='utf-8')
json.dump(dic_obj, fp=fp, ensure_ascii=False)
fp.close()
# 由于得到的json是中文,不能使用acsii编码
这里存在一个问题:现在通过开发者工具获取到的url并不能使用此方法获取到翻译内容,无法获取可爬取的域名子目录。
百度翻译的“https://fanyi.baidu.com/v2transapi”这个地址有两个反爬机制sign和token,参考学习。
实例:爬取豆瓣电影分类排行榜
对豆瓣电影排行榜中的动画类型电影数据进行爬取。
前期步骤:
(1)打开对应页面:打开豆瓣电影–》排行榜–》动画
(2)打开开发者工具–》向下滑动网页页面直到开发者工具中出现请求数据包
(3)点击数据包,查看content-type响应头信息
可知是一组json数据,再查看response
发现其中存放了电影的详情数据,所以确定可以通过这个数据包的url传入相应的参数获取需要的电影信息。
(4)url中的参数处理
以下为获取到的url
在编程时将url的参数封装在字典中传入,即删除“?”后内容(包括“?”)
传入
import requests
import json
if __name__ == "__main__":
url = 'https://movie.douban.com/j/chart/top_list'
# 将url中的参数使用字典封装,参数直接使用
param = {
'type': '25',
'interval_id': '100:90',
'action': '',
'start': '0', # start表示从库中的第几部电影取
'limit': '20' # limit表示一次取出的个数
}
headers = {
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)'
}
response = requests.get(url=url, params=param, headers=headers)
list_data = response.json()
fp = open('./douban.json', 'w', encoding='utf-8')
json.dump(list_data, fp=fp, ensure_ascii=False)
fp.close()
作业:爬取肯德基餐厅位置信息
前期步骤:
(1)进入肯德基餐厅信息查询页面,查看当前网址。
(2)餐厅关键字输入“北京”,查询,再次查看当前网址。如果网址不发生改变则说明发起的请求是一个ajax请求,改变则说明不是。
(3)观察ajax请求数据包的参数
pageIndex表示第一页,pageSize表示第一页显示的数据量,keyword和pageIndex都可以写为动态的。
(4)观察响应头信息
是一个text文本数据
import requests
if __name__ == "__main__":
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx'
# 将url中的参数使用字典封装,参数直接使用
param = {
'op': 'keyword',
'cname':'',
'pid': '',
'keyword': '北京',
'pageIndex': '1',
'pageSize': '10'
}
headers = {
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)'
}
response = requests.get(url=url, params=param, headers=headers)
page_text = response.text
with open('./kfc.txt', 'w', encoding='utf-8') as fp:
fp.write(page_text)
实例:爬取国家药品监督管理总局中基于中华人民共和国化妆品生产许可证相关数据
爬取企业化妆品生产许可证的详情数据。
从之前的实例中就可以发现,获取的爬虫数据并非是从浏览器地址栏所访问的网页地址中获取,而是从ajax动态请求得到的。这些由其他方式请求得到,而并非由地址栏的url请求得到的数据被称为动态加载出来的数据。
前期步骤:
(1)打开国家药品监督管理局,化妆品生产许可信息管理系统服务平台,使用开发者工具抓取到数据包
其中名为xk/的包同地址栏的url尾部相同,点击查看其response,发现并不包含企业信息。
(2)尝试点击XHR类型请求,发现其response内含有所要爬取的数据。即这个ajax请求数据包所对应的url可以用于爬取数据,且该url请求得到json类型数据。
(3)由于需要抓取所有企业数据的许可证详情信息,所以需要获取当前页面所有企业详情页的url,但是对搜索到的json进行观察,发现并不包含企业详情页的url,但是观察可以发现json串中有名为ID的信息
(4)打开第一个企业信息,可以从地址栏得到该企业详情页url,观察发现url中的参数id对应之前搜索到的企业ID
再打开其他企业的详情页,发现详情页url的域名相同,携带参数id不同。
(5)结论1:可以先从首页对应的ajax请求得到的json串中获取id,在将域名和id值进行拼接得到详情页的url。但是还有一个问题,就是详情页是否也是动态加载数据。通过开发者工具的搜索观察,发现详情页确实也是动态加载数据,那么就不能使用拼接成的url来爬取详情数据。
(6)观察企业详情页的XHR类型请求,通过ajax请求数据包得到url,响应数据类型,参数
结论2:发现企业详情页的ajax请求数据包的url均相同,只有参数id不同。即如果可以批量获取多家企业的id后,就可以将id和url形成一个完整的详情页对应详情数据的ajax的url,得到json数据。
(7)分析json串,可得json串内容是一个字典,需要key为“list”的value值,“list”的value值为一个列表,且列表内元素为字典。
import requests
import json
if __name__ == "__main__":
url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
# 封装参数
data = {
'on': 'true',
'page': '1',
'pageSize': '15',
'productName': '',
'conditionType': '1',
'applyname': ''
}
headers = {
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)'
}
# 取出所需要的id,放入id_list中
id_list = []
all_data_list = [] # 存储所有企业的详情数据
json_ids = requests.post(url=url, headers=headers, data=data).json()
for dic in json_ids['list']:
id_list.append(dic['ID'])
# print(id_list) # 测试是否得到企业id
# 获取企业详情数据
post_url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
for id in id_list:
data = {
'id': id
}
detail_json = requests.post(url=post_url, headers=headers, data=data).json()
all_data_list.append(detail_json)
# 持久化存储
fp = open('./allData.json','w',encoding='utf-8')
json.dump(all_data_list, fp=fp, ensure_ascii=False)
fp.close()
获取多页数据可以在获取id代码处加for循环,不过建议获取多页时加time.sleep()等待,防止访问速度过快被服务器屏蔽。