本章节所有感悟来自:Python爬虫7天速成(2020全新合集)无私分享 Python
视频是2020年,我是2021年看得视频,网站的更新速度较快,其中2.6章节中的药品监督总局网址已经不是视频中的网址我已经做个笔记和更改,希望帮助到想学习爬虫的你们
- 通过编写程序、模拟浏览器上网,然后让其模拟浏览器去上网,然后让其去互联网上抓取数据的过程。
- 可以认为浏览器上网就是一个原始的、天然的爬虫工具。
爬虫的两种方式:
- 浏览器
- 编写代码
- 在法律中是不被禁止的
- 具有违法风险
- 善意爬虫 恶意爬虫
- 爬虫干扰了别访问网站的正常运营
- 爬虫抓取了受到法律保护的特定类型的数据或信息
- 时常的优化自己的程序,避免干扰被访问网站的正常运行
- 在使用,传播爬取到二到数据时,审查抓取到的内容,如果发现了涉及到用户隐私或者商业机密等敏感内容,需要及时停止爬取或传播
- 通用爬虫
抓取系统重要组成部分。抓取的是一整张页面数据- 聚焦爬虫
是建立在通用爬虫的基础之上。抓取的是页面中特定的局部内容。- 增量式爬虫⭐
检测网站中数据更新的情况。只会抓取网站中最新更新出来的数据。
- 反爬机制
相关的门户网站,可以指定相应的策略或者技术手段,防止爬虫程序进行网站数据的爬取。- 反反爬策略
爬虫程序可以通过制定相关的策略或者技术手段,破解门户网站中具备的反爬机制,从而可以获取门户网站中相关的数据。
robots.txt
协议其实就是一项君子协议。
此协议就规定了网站哪些数据可以被爬虫爬取,哪些数据不可以被爬虫爬取。
既可以保护网站隐私数据,又可以被搜索引擎收录,增加相关网站的流量。
超文本传输协议
概念:就是服务器和客户端进行数据交互的一种形式。
请求头信息
- User-Agent:请求载体的身份标识
例如:1. 使用Google浏览器浏览一个网址,则Google浏览器的身份就是当前载体的身份标识,这个身份标识即可以使用一个字符串来表示。2. 若是使用编写代码方式发起一个url的请求则标识就不是上述字符串- Connection:请求完毕之后,是断开连接还是保持连接
- close
- keep live
响应头信息
- Content-Type:服务器响应回客户端的数据类型
可以是字符串、Json等
http是安全的,https是不安全的额
安全的超文本传输协议,数据传输和交互的时候是经过加密的
客户端需要把发送给服务器的数据进行加密(加密方式是由客户端自己指定的),然后传输密文和解密的密钥给服务器,服务器接收后可以根据密钥对密文进行解密,解密之后,服务器就可拿到原始数据
分析:若密文和密钥被拦截,则密文就会被密钥解密,数据就会被暴露。
https所采用的密钥加密就是该方式
- urllib模块 (古老的模块)
- request模块⭐(重点学习)
request模块
:python中原生的一款基于网络请求的模块,功能非常强大,简单便捷,效率极高。
作用:模拟浏览器发送请求。
一定要严格遵从浏览器发送请求流程:
- 指定url(具体网址)
- 发起请求(类似浏览器地址栏输入网址按下回车–GET请求)
- 获取相应数据(回车之后,浏览器会有一页数据)
- 持久化存储–响应数据(request模块在互联网上爬取的页面数据)
pip install requests
import requests
if __name__ == "__main__":
# 1. 指定url
url = "https://www.sogo.com/"
# 2. 发起请求
response = requests.get(url=url)
# get方法灰返回一个响应对象
# 3. 获取相应数据 .text返回的是字符串形式的响应数据
page_text = response.text
print(page_text)
# 4. 持久化存储 --page_text
with open('./sogo.html', 'w',encoding='utf-8') as fp:
fp.write(page_text)
print('爬取数据结束')
爬取搜狗指定词条对应的都多结果页面
# 反爬机制:UA检测
# UA检测对应的反反爬策略:#UA伪装
# UA:User-Agent(请求载体的身份标识)
'''
UA检测:门户网站的服务器会检测对应请求的载体身份标识,如果检测到请求的载体身份标识是某一款浏览器,
那么就说明该请求是一个正常的请求。但是,如果检测到请求的载体身份标识不是基于某一款浏览器的,
则表示该请求为不正常的请求(爬虫),z则服务器端就很有可能拒绝该次请求
'''
#UA伪装:让爬虫对应的请求载体身份标识伪装成一款浏览器
import requests
if __name__ == '__main__':
#UA伪装:将对应的User-Agent封装到一个字典中
# 伪装成Google浏览器
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
}
# 1. 指定url
url = 'https://www.sogou.com/sogou?'
# 'https://www.sogou.com/sogou?query=波晓张'这样也可以的
# 处理url携带的参数 通常情况url不可能只携带一组参数
# 处理:封装到字典中
key = input('enter a word:')
param = {
'query':key
}
# 2. 对指定的url发起请求 对应的url是携带参数的,并且请求过程中处理了参数
response = requests.get(url,params=param,headers=headers)
# 3. 获取响应数据
page_txt = response.text
filename = key+'.html'
with open(filename,'w',encoding='utf-8') as fp:
fp.write(page_txt)
print(filename,'保存成功!!!')
处理乱码后,页面显示【异常访问请求】导致请求数据的缺失
异常访问的请求
- 网站后台已经检测出该次请求不是通过浏览器发起的请求而是通过爬虫程序发起的请求(不是浏览器发起的请求都是异常请求)
录入单词之后,没有触碰任何的点击事件。当前页面就变化了,也就是进行了局部刷新(
AJAX请求
)
通过
浏览器抓包
(F12)了解到:
- 对应请求为
POST请求
(携带了参数)- 响应数据是一组json数据
import requests
import json
if __name__ == '__main__':
# 1. 指定url
# post请求
post_url = 'https://fanyi.baidu.com/sug'
# 2. 进行UA伪装
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
}
# 3. post请求参数处理(同get)
word = input('enter a word:')
data = {
'kw':word
}
# 4. 请求发送
respose = requests.post(url=post_url,data=data,headers=headers)
# 5. 获取响应数据 由浏览器抓包工具可知返回的是json数据
# 此处若用respose.text 则返回的是字符串
# print("text",respose.text)
dic_obj = respose.json()# json()方法返回的是obj(如果确认响应数据是json类型,才可以使用json())
# print("dic_obj",dic_obj)
# 6. 持久化存储
filename = word+'.json'
fp = open(filename,'w',encoding='utf-8')
json.dump(dic_obj,fp=fp,ensure_ascii=False) ①# 拿到的json字符串是不能用ASCII进行编码的
print('over!!!')
如上图搜索并点击进入相应内容,进入相应页面后可以点击
类型:喜剧
,之后通过滑动滚轮后发现滑动到底部后发现:
- 地址栏没有发生变化
- 右边的矩形灰色栏一到底部,底部就会出现新的电影数据而且矩形灰色栏回跳动到中间位置
因此,我们判断滚轮滑动到底部,页面进行了
AJAX请求
思考:基于抓包工具进行全局搜索不一定可以每次都能定位到动态加载数据对应的数据包?
原因:如果动态加载的数据是经过加密的密文数据
import requests
import json
if __name__ == '__main__':
url = 'https://movie.douban.com/j/chart/top_list'
param = {
'type': '24',
'interval_id': '100:90',
' action': '',
'start': '0', # 从库中第几部电影去取
'limit': '20', # 第一次取出的个数
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
}
# 根据浏览器抓包工具进行请求类型判断
response = requests.get(url=url,params = param,headers = headers)
# 由浏览器判断返回类型是列表
list_data = response.json()①
fp = open('./douban.json','w',encoding='utf8')
json.dump(list_data,fp,ensure_ascii=False)
print('over!!!')
肯德基官网
同理,进入地址搜索栏后,输入地址若地址栏没有发生变化则页面是进行的AJAX请求,反之则不是AJAX请求
结论:该页面发生了
AJAX请求
import requests
if __name__ == '__main__':
# 爬取的是第一页数据
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
data = {
'cname': '',
'pid': '',
'keyword': '北京',
'pageIndex': str(page),# 这里最好是字符串形式
'pageSize': '10',
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
}
respose = requests.post(url=url,data=data,headers=headers)
page_text = respose.json()
# 打印餐厅信息 - 名字 地址
for dic in page_text['Table1']:
title = dic['storeName']
addr = dic['addressDetail']
print(title,addr)
# 这只是第一页数据,如何拿到第二页、第三页数据........?
import requests
if __name__ == '__main__':
# 爬取多页
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
# 拿到北京八页数据
for page in range(1,9):
data = {
'cname': '',
'pid': '',
'keyword': '北京',
'pageIndex': str(page),# 这里最好是字符串形式
'pageSize': '10',
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
}
respose = requests.post(url=url,data=data,headers=headers)
page_text = respose.json()
# 打印餐厅信息 - 名字 地址
for dic in page_text['Table1']:
title = dic['storeName']
addr = dic['addressDetail']
print(title,addr)
化妆品生产许可信息管理系统服务平台 - NMPA
用如下代码发起请求:
import requests
if __name__ == "__main__":
url = ' http://scxk.nmpa.gov.cn:81/xk/'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
}
page_text = requests.get(url=url, headers=headers).text
with open('./huazhuangpin.html','w',encoding='utf8') as fp:
fp.write(page_text)
结论:页面中的数据一定不是http://scxk.nmpa.gov.cn:81/xk/这个url请求到的,是别的请求方式请求到的
- (很有可能是
AJAX
请求)
利用在线JSON解析工具发现:
- 首页对应的企业信息数据是通过ajax请求得到的
如下图,通过点击首页的公司发现
- url的域名都是一样的,只有携带的参数(id)不一样
- id值可以从首页对应的ajax请求到的json串中获取
- 域名和id值拼接出一个完整的企业对应的详情页的url
<-------------------------------------------手动分割----------------------------------------------->
那上图获得的公司对应的化妆品生产许可证
信息页面数据
也是AJAX请求到的吗?
我们需要做相关验证:
- 代码验证(笨方法)
- 抓包工具验证
结论:我们不可能是通过对公司的化妆品生产许可证
信息页面数据
的url发请求获得数据的(看图)
json解析如下 :与详情页数据一致
{
"businessLicenseNumber": "91320213355032183D",
"businessPerson": "邱国忠",
"certStr": "一般液态单元(护肤水类);膏霜乳液单元(护肤清洁类)",
"cityCode": "",
"countyCode": "",
"creatUser": "",
"createTime": "",
"endTime": "",
"epsAddress": "无锡市北塘区金山四支路11-1-12号",
"epsName": "无锡邦士立生物科技有限公司",
"epsProductAddress": "无锡市北塘区金山四支路11-1-12号",
"id": "",
"isimport": "N",
"legalPerson": "邱国忠",
"offDate": "",
"offReason": "",
"parentid": "",
"preid": "",
"processid": "202102240853582029gbgi",
"productSn": "苏妆20160013",
"provinceCode": "",
"qfDate": "",
"qfManagerName": "江苏省药品监督管理局",
"qualityPerson": "宗同祥",
"rcManagerDepartName": "江苏省药品监督管理局(无锡检查分局)",
"rcManagerUser": "王丹、冯佳超",
"startTime": "",
"warehouseAddress": "",
"xkCompleteDate": null,
"xkDate": "2026-04-20",
"xkDateStr": "2021-04-20",
"xkName": "张贤",
"xkProject": "",
"xkRemark": "",
"xkType": "202"
}
对不同的公司进行上述操作发现:
- 所有的post请求的url都是一样的,只有参数id值是不同的。
- 如果我们可以批量获取多家企业的id后,就可以将id和url形成一个完整的详情页对应详情数据的ajax请求的url
import requests
import json
if __name__ == "__main__":
# 批量获取企业id值
url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
# 参数的封装
data = {
'on': 'true',
'page': '1',# 页码
'pageSize': '15',# 每页显示的数据
'productName': '',
'conditionType': '1',
'applyname': '',
'applysn':'',
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
}
id_list = [] # 存储企业的id
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(len(id_list)) # 15
# 获取企业详情数据
post_url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
for id in id_list:
data_id ={
'id':id
}
# 发起请求
detail_json = requests.post(url=post_url, data=data_id,headers=headers).json()
# print(detail_json,'---------------ending------------')
# 存储到列表
all_data_list.append(detail_json)
# 持久化存储
fp = open('./allCompanyData.json','w',encoding='utf-8')
json.dump(all_data_list,fp,ensure_ascii=False)
print('over!!!')
import requests
import json
if __name__ == "__main__":
# 批量获取企业id值
url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
}
id_list = [] # 存储企业的id
all_data_list = [] # 存储所有企业的详情数据
for page in range(1,6):
# 参数的封装
data = {
'on': 'true',
'page': str(page),# 页码
'pageSize': '15',# 每页显示的数据
'productName': '',
'conditionType': '1',
'applyname': '',
'applysn':'',
}
# 字典类型
json_ids = requests.post(url=url, headers=headers,data=data).json()
for dic in json_ids['list']:
id_list.append(dic['ID'])
# print(len(id_list)) # 15
# 获取企业详情数据
post_url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
for id in id_list:
data_id ={
'id':id
}
# 发起请求
detail_json = requests.post(url=post_url, data=data_id,headers=headers).json()
# print(detail_json,'---------------ending------------')
# 存储到列表
all_data_list.append(detail_json)
print(len(all_data_list))
# 持久化存储
fp = open('./allCompanyData.json','w',encoding='utf-8')
json.dump(all_data_list,fp,ensure_ascii=False)
print('over!!!')
①json.dump()
交换参数不影响函数的使用
下节内容:
数据解析
- 聚焦爬虫
- 正则
- bs4
- xpath