爬虫
定义:
-
什么是爬虫?
- 是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。
-
爬虫的分类:
- 通用爬虫:爬行对象从一些种子 URL 扩充到整个 Web,主要为门户站点搜索引擎和大型 Web 服务提供商采集数据。
- 聚焦爬虫:是指选择性地爬行那些与预先定义好的主题相关页面的网络爬虫。
- 增量式爬虫:指对已下载网页采取增量式更新和只爬行新产生的或者已经发生变化网页的爬虫,它能够在一定程度上保证所爬行的页面是尽可能新的页面。
-
规避风险:
- 严格遵守网站设置的robots协议;
- 在规避反爬虫措施的同时,避免干扰了被访问网站的正常运营;;
- 在抓取了受到法律保护的特定类型的数据或信息(如用户的个人信息、隐私或者他人的商业秘密的),应及时停止并删除。
一. Python爬虫页面分析
输入域名->下载源代码->分析需求->保存数据
二. requests模块的基本使用
1. 安装requests
pip install requests
-
文档地址:
- https://requests.readthedocs.io/zh_CN/latest/
-
开源地址:
- https://github.com/kennethreitz/requests
-
参考博客:
-
https://www.jianshu.com/p/ecb4d54ad8cf
-
https://www.cnblogs.com/happymeng/p/10112374.html
-
2. requests模块的编码流程
-
1.导入模块
-
2.指定url
-
3.发起请求
-
4.获取响应数据
-
5.持久化存储
-
Example
import requests #1.导入模块
url = 'https://www.sogou.com/' #2.指定url
response = requests.get(url=url) #3.请求发送get:get返回值是一个响应对象
page_txt = response.text #4.获取响应数据,text返回的是字符串形式的响应数据
with open('sogou.html','w',encoding='utf-8') as fp: #5. 持久化存储
fp.write(page_txt)
#如何爬取图片
import requests
url='https://pic.qiushibaike.com/system/pictures/12223/122231866/medium/IZ3H2HQN8W52V135.jpg'
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
}
img_data = requests.get(url,headers=headers).content #byte类型数据
with open('./img.jpg','wb') as fp:
fp.write(img_data)
#弊端:不能使用UA伪装
from urllib import request
url='https://pic.qiushibaike.com/system/pictures/12223/122231866/medium/IZ3H2HQN8W52V135.jpg'
request.urlretrieve(url,filename='./qiutu.jpg')
3. 查看响应内容
# response.text 返回的是Unicode格式的数据。使用时,Requests 会基于 HTTP 响应的文本编码自动解码响应内容,大多数 Unicode 字符集都能被无缝地解码。
print (response.text)
# response.content返回的字节流数据,返回的是服务器响应数据的原始二进制字节流,可以用来保存图片等二进制文件。
print (respones.content)
# 查看完整url地址
print (response.url)
# 查看响应头部字符编码
print (response.encoding)
# 查看响应码
print (response.status_code)
# 如果响应是json数据的查看
print(response.json())
4. 添加参数
(1) 添加 headers
#UA检测:门户网站通过检测请求载体的身份标识判定改请求是否为爬虫发起的请求
#UA伪装:添加headers的参数。浏览器中查看User-Agent
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
}
- Example
import requests
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
}
url = 'https://www.sogou.com/'
response = requests.get(url=url,headers=headers)
(2) 添加查询参数
-
get(url,params,headers)
-
example
import requests headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36', } url = 'https://www.sogou.com/' params = { 'query':'python' } response = requests.get(url=url,params=params,headers=headers) response.encoding = 'utf-8' #修改响应数据的编码格式,解决乱码问题 page_text = response.text
-
-
post(url,data,headers)
-
Example
import requests headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36', } #肯德基餐厅查询http://www.kfc.com.cn/kfccda/storelist/index.aspx url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword' for page in range(1,3): data = { 'cname': '', 'pid': '', 'keyword': '西安', 'pageIndex': str(page), 'pageSize': '10', } response = requests.post(url=url,headers=headers,data=data) print(response.json())
-
5. 常见问题
(1) 当前页面的内容在响应的文档中找不到
-
分析:可能动态加载的页面数据(是通过另一个单独的请求请求到的数据)
-
常见场景:当滚动条被滑动到页面底部的时候,当前页面发生了局部刷新(ajax的请求)
-
如何检测页面中是否存在动态加载的数据?
-
基于抓包工具实现
-
先捕获网站请求后所有的数据包
-
在数据包中定位到地址栏所对应请求的数据包,在response选项卡对应的数据中进行局部搜索(页面中的某一组内容)
- 可以搜索到:爬取的数据不是动态加载的
- 没有搜索到:爬取的数据是动态加载的
-
如何定位动态加载的数据在哪个数据包中呢?
- 进行全局搜索
-
-
解决:找到动态加载的请求地址,发送请求,获取数据
(2) 乱码问题
- 修改响应数据的编码格式
- 比如:
response.encoding = 'utf-8'
三. 数据解析的方式
1. 正则表达式re模块
参考网页:
- https://deerchao.cn/tutorials/regex/regex.htm
2. BeautifulSoup4
参考文档:
- https://www.crummy.com/software/BeautifulSoup/
- https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/
参考博客:
- https://blog.csdn.net/luanpeng825485697/article/details/78378653
流程:
- 安装
pip install bs4
pip install lxml
- 将一个html文档中的数据加载文档传入BeautifulSoup 的构造方法,就能得到一个文档的对象(实例化)。
from bs4 import BeautifulSoup #导入
soup=BeautifulSoup(html文档, "lxml") #得到文档的对象
3.对象分析
解析以后全部html代码转变为4种类型:
-
基本对象类型
-
Tag——标签,最基本的信息组织单元,分别用<>和>表明开头和结尾
-
标签Name属性——标签的名字,
...
的名字是'p',格式: .name -
标签Attributes属性——标签的属性,字典形式组织,格式: .attrs
-
NavigableString——标签内非属性字符串,<>...>中的字符串,格式: .string
-
Comment——标签内字符串的注释部分,一种特殊的Comment类型(尖括号叹号表示注释开始: )
-
BeautifulSoup对象(整个html对象soup)
-
-
-
常用方法
-
定位标签的操作:
- soup.tagName:定位到第一个出现的tagName标签
soup.a 第一个a标签
- soup.tagName:定位到第一个出现的tagName标签
-
属性定位:soup.find('tagName',attrName='value')
-
```soup.find("a", class_="current")) 标签属性联合搜索,class 是 python 的关键词,所以class后面加了_``````
-
``````soup.find("a", id="link3")```
-
-
属性定位:soup.find_all('tagName',attrName='value')
- 返回值为列表,其他用法同find
-
css选择器定位:soup.select('选择器')
soup.select('ul') #按名称 soup.select('.current') #按类名 soup.select('#app') #按id soup.select('li .current') #后代查询 soup.select("head > title") #子标签查询 soup.select('a[class="current"]') #属性查询
-
按文本搜索
-
soup.find_all(text="首页") #按文本搜素,接受的参数与按标签搜索一样
-
soup.find_all("a", limit=2,recursive=False) #所有搜索都可以用limit限定最大搜索到的数目,用recursive限定只搜索直接子节点
-
-
取文本
-
.string:获取直系的文本内容
-
.text:获取所有的文本内容
-
-
取属性
- tagName['attrName']
-
3. XPath
- 定义:
XPath,全称 XML Path Language,即 XML 路径语言,它是一门在XML文档中查找信息的语言。XPath 最初设计是用来搜寻XML文档的,但是它同样适用于 HTML 文档的搜索。
- 参考博客:
- https://www.runoob.com/xpath/xpath-tutorial.html
- https://www.jianshu.com/p/b80e9cd81d7a
- 流程:
- 安装
pip install lxml
2. 将一个html文档中的数据加载文档传入BeautifulSoup 的构造方法,就能得到一个文档的对象(实例化)。
from lxml import etree #导入
tree = etree.HTML(page_text) #得到文档的对象,etree对象的实例化
3. 对象分析
- 在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。
- 常用方法
- etree.tostring(tree)
- 把etree对象的实例化,转化为html字符串,方便分析文档
- etree.tostring(tree).decode("utf-8") 把etree对象的实例化,转化为html字符串并格式化,方便分析文档
- xpath表达式:xpath方法的返回值一定是一个列表
- 最左侧的/表示:xpath表达式一定要从根标签逐层进行标签查找和定位
- 最左侧的//表示:xpath表达式可以从任意位置定位标签
- 非最左侧的/:表示一个层级
- 非最左侧的//:表示夸多个层级
- 属性定位://tagName[@attrName="value"]
- 索引定位://tagName[index] 索引是从1开始
- 取文本:
- xpath方法返回的是选择器对象列表,extract()用于从选择器对象中提取数据.比如:xpath('./p/text()')[0].extract()
- xpath结果为只含有一个值的列表,可以使用extract_first().比如:xpath('./p/text()').extract_first()
- /text():直系文本内容
- //text():所有的文本内容
- 选择器对象.xpath('string(.)').strip() :获取元素下的所以内容
- 取属性:
- /@attrName
解决xpath中etree.tostring中文乱码
利用xpath建标签树以后,虽然提高了元素匹配效率,但是etree会把中文转为ASCII码,所以简单地tostring以后会有乱码。
etree.tostring(tree, encoding='utf-8', pretty_print=True, method="html").decode('utf-8')
练习
1. 使用正则进行首页图片下载
import requests
import re
import os
url='https://www.mzitu.com/'
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
}
page_txt=requests.get(url=url,headers=headers).text #获取网页文本内容
#分析网页文本内容,确定正则表达式
ex ="![(.*?)](.*?)
2. 使用BeautifulSoup4爬取儒林外史整篇内容
#爬取儒林外史整篇内容(章节名称+章节内容)http://www.shicimingju.com/book/rulinwaishi.html
import requests
from bs4 import BeautifulSoup #导入BeautifulSoup
main_url='http://www.shicimingju.com/book/rulinwaishi.html'
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
}
page_txt=requests.get(url=main_url,headers=headers).text #获取网页文本内容
soup=BeautifulSoup(page_txt, "lxml") #得到文档的对象
#列表的方式获取a标签对象(a里面有每章节的网址和标题)
a_list = soup.select('.book-mulu ul li a')
for tmp_a in a_list:
title=tmp_a.string #章节的标题
detail_url='http://www.shicimingju.com'+tmp_a['href'] #章节的网址
detail_txt=requests.get(url=detail_url,headers=headers).text #获取网页文本内容
soup=BeautifulSoup(detail_txt, "lxml") #得到文档的对象
content=soup.select('.chapter_content')[0].text #分析文档对象,直接获取每章节的内容
#保存文件
with open('./rulinwaishi.txt','a+',encoding='utf-8') as fp:
fp.write(title+':'+content+'\n')
print(title,"下载成功")
3. xpath练习(中文乱码的处理)
#http://pic.netbian.com/4kmeinv/中文乱码的处理
import requests
from lxml import etree
import os
#创建存放目录
dirName = './meinvLibs'
if not os.path.exists(dirName):
os.mkdir(dirName)
#分析,处理url(当需要爬的第一页和第二、三页不同时)
url = 'http://pic.netbian.com/4kmeinv/index_1.html'
for page in range(1,3):
if page == 1:
new_url = 'http://pic.netbian.com/4kmeinv/'
else:
new_url = format(url%page)
response = requests.get(new_url,headers=headers)
print(requests.get(new_url,headers=headers).text) #发现出现乱码,查看响应,发现编码方式是gbk,进行乱码处理
print(response.encoding) #发现导出编码方式为'ISO-8859-1',而网页的解码编码方式为gbk,所有需要gbk解码。
response.encoding='gbk'
page_text = response.text #print(page_text),查看发现处理成功
tree = etree.HTML(page_text)
#print(etree.tostring(tree, encoding='utf-8', pretty_print=True, method="html").decode('utf-8'))
a_list = tree.xpath('//div[@class="slist"]/ul/li/a')
for a in a_list:
img_src = 'http://pic.netbian.com'+a.xpath('./img/@src')[0]
img_name = a.xpath('./b/text()')[0]
print(img_name)
#img_name = img_name.encode('iso-8859-1').decode('gbk') #前面不处理,这里也可以处理。由于乱码是iso-8859-1,解码是gbk,所以要先编码,在解码乱码
img_data = requests.get(img_src,headers=headers).content
imgPath = dirName+'/'+img_name+'.jpg'
with open(imgPath,'wb',) as fp:
fp.write(img_data)
print(img_name,'下载成功!!!')