目录
-
网络爬虫概述
-
爬虫请求模块
-
URL地址编码模块
-
正则解析模块re
-
requests模块
-
xpath解析
-
lxml解析库
-
常见的反爬机制及处理方式
-
cookie模拟登录
-
json解析模块
-
scrapy框架
-
猫眼电影案例
-
总结
由于本文篇幅过长,将分为两篇进行总结。
一、网络爬虫概述
1、定义
网络蜘蛛、网络机器人,抓取网络数据的程序。
其实就是用Python程序模仿人点击浏览器并访问网站,而且模仿的越逼真越好。
2、爬取数据目的
-
获取大量数据,用来做数据分析
-
公司项目的测试数据,公司业务所需数据
3、企业获取数据方式
-
公司自有数据
-
第三方数据平台购买
-
爬虫爬取数据 : 第三方平台上没有,或者价格太高
4、Python做爬虫优势
-
Python :请求模块、解析模块丰富成熟,强大的Scrapy网络爬虫框架
-
PHP :对多线程、异步支持不太好
-
JAVA:代码笨重,代码量大
-
C/C++:虽然效率高,但是代码成型慢
5、爬虫分类
A、通用网络爬虫(搜索引擎使用,遵守robots协议)
robots协议 :网站通过robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取,通用网络爬虫需要遵守robots协议(君子协议)
https://www.taobao.com/robots.txt
B、聚焦网络爬虫 :自己写的爬虫程序
6、爬虫爬取数据步骤
-
确定需要爬取的URL地址
-
由请求模块向URL地址发出请求,并得到网站的响应
-
从响应内容中提取所需数据:
所需数据,保存
页面中有其他需要继续跟进的URL地址,继续第2步去发请求,如此循环
二、爬虫请求模块
1、模块名及导入urllib.request
模块名:urllib.request
导入方式:
import urllib.request
from urllib import request
使用方法:
req = request.Request(url,headers=headers)
res = request.urlopen(req)
html = res.read().decode('utf-8')
2、常用方法详解
urllib.request.urlopen()方法
作用
向网站发起请求并获取响应对象
参数
URL:需要爬取的URL地址
timeout: 设置等待超时时间,指定时间内未得到响应抛出超时异常
响应对象(response)方法
-
bytes = response.read()
-
string = response.read().decode(‘utf-8’)
-
url = response.geturl() 返回实际数据的URL地址
-
code = response.getcode() HTTP响应码
-
补充
string.encode() # string -> bytes
bytes.decode() # bytes -> string
urllib.request.Request()
作用
创建请求对象(包装请求,重构User-Agent,使程序更像正常人类请求)
参数
URL:请求的URL地址
headers:添加请求头(爬虫和反爬虫斗争的第一步)
使用流程
- 构造请求对象(重构User-Agent)
req = urllib.request.Request(url=url,headers={'User-Agent':'Mozilla/5.0 xxxx'})
- 发请求获取响应对象(urlopen)
res = urllib.request.urlopen(req)
- 获取响应对象内容
html = res.read().decode('utf-8')
示例:向测试网站发起请求,构造请求头并从响应中确认请求头信息
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地址编码模块
1、模块名
urllib.parse
作用
给URL地址中查询参数进行编码
编码前:https://www.baidu.com/s?wd=美女
编码后:https://www.baidu.com/s?wd=%E7%BE%8E%E5%A5%B3
常用方法
- urlencode({dict})
urlencode({'wd':'美女','pn':'20'})
#编码后 :'wd=%E8%D5XXX&pn=20'
- quote(string)
quote('织女')#编码后 :'%D3%F5XXX'
unquote('%D3%F5XXX')
#解码后:织女
使用方法:
from urllib import parse
#-----------urlencode({dict})编码-----------------
query_string = {'wd' : '美女'}
result = parse.urlencode(query_string)
# result: 'wd=%e7%be%8e%e5%a5%b3'
#----------quote(string)编码------------------
string = '美女'
print(parse.quote(string))
# 结果: %E7%BE%8E%E5%A5%B3
#----------unquote(string)解码----------------
string = '%E7%BE%8E%E5%A5%B3'
result = parse.unquote(string)
print(result)
2、拼接URL地址的3种方式
- 字符串相加
'https://www.baidu.com/s?' + urlencode({'wd':'美女','pn':'50'})
- 字符串格式化(占位符)
'https://www.baidu.com/s?%s' % urlencode({'wd':'美女','pn':'50'})
- format()方法
'https://www.baidu.com/s?{}'.format(urlencode({'wd':'美女','pn':'50'}))
3、总结
urllib.request
urllib.request.Request(url=url,headers=headers)
urllib.request.urlopen(req)
urllib.parse
urllib.parse.urlencode({'wd':'美女'})
urllib.parse.quote('美女')
四、正则解析模块re
1、re模块使用流程
方法一
r_list=re.findall('正则表达式',html,re.S)
方法二
创建正则编译对象
pattern = re.compile('正则表达式',re.S)
r_list = pattern.findall(html)
2、正则表达式元字符
3、推荐使用方法
# 匹配任意一个字符的正则表达式
import re
# 方法一
pattern = re.compile('.',re.S)
# 方法二
pattern = re.compile('[\s\S]')
4、贪婪匹配和非贪婪匹配
贪婪匹配(默认)
-
在整个表达式匹配成功的前提下,尽可能多的匹配 * + ?
-
表示方式:.* .+ .?
非贪婪匹配
-
在整个表达式匹配成功的前提下,尽可能少的匹配 * + ?
-
表示方式:.*? .+? .??
5、正则表达式分组
作用
在完整的模式中定义子模式,将每个圆括号中子模式匹配出来的结果提取出来
示例
import re
s = 'A B C D'
p1 = re.compile('\w+\s+\w+')
print(p1.findall(s))
# ['A B','C D']
p2 = re.compile('(\w+)\s+\w+')
print(p2.findall(s))
# ['A','C']
p3 = re.compile('(\w+)\s+(\w+)')
print(p3.findall(s))
# ['A B','C D']
# [('A','B'),('C','D')]
6、分组总结
-
在网页中,想要什么内容,就加()
-
先按整体正则匹配,然后再提取分组()中的内容。如果有2个及以上分组(),则结果中以元组形式显示 [(),(),()]
7、字符串常用方法
# 'hello world'.strip() --> 'hello world'
# 'hello world'.split(' ') --> ['hello','world']
# 'hello world'.replace(' ','#') -> 'hello#world'
五、requests模块
1、安装
Windows
# 方法一
进入cmd命令行 :python -m pip install requests
# 方法二
右键管理员进入cmd命令行 :pip install requests
2、常用方法requests.get()
作用
# 向网站发起请求,并获取响应对象
res = requests.get(url,headers=headers)
参数
-
url :需要抓取的URL地址
-
headers : 请求头
-
timeout : 超时时间,超过时间会抛出异常
响应对象(res)属性
-
encoding :响应字符编码
res.encoding = ‘utf-8’
-
text :字符串
-
content :字节流
-
status_code :HTTP响应码
-
url :实际数据的URL地址
# 方式一
res = requests.get(url,headers=headers)
res.encoding = 'utf-8'
html = res.text
# 方式二
res = requests.get(url,headers=headers)
html = res.content.decode('utf-8')
非结构化数据保存
with open('xxx.jpg','wb') as f:
f.write(res.content)
3、查询参数-params
参数类型
字典,字典中键值对作为查询参数
使用方法
res = requests.get(url,params=params,headers=headers)
特点:
* url为基准的url地址,不包含查询参数
* 该方法会自动对params字典编码,然后和url拼接
示例
import requests
baseurl = 'http://tieba.baidu.com/f?'
params = {
'kw' : '赵丽颖吧',
'pn' : '50'
}
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)'}
# 自动对params进行编码,然后自动和url进行拼接,去发请求
res = requests.get(baseurl,params=params,headers=headers)
res.encoding = 'utf-8'
print(res.text)
requests模块参数总结
-
url
-
params : {}
-
proxies: {}
-
auth: ()
-
verify: True/False
-
timeout
4、requests.post()
1、适用场景
Post类型请求的网站
2、参数-data
response = requests.post(url,data=data,headers=headers)
# data :post数据(Form表单数据-字典格式)
3、请求方式的特点
# 一般
GET请求 : 参数在URL地址中有显示
POST请求: Form表单提交数据
六、xpath解析
1、定义
XPath即为XML路径语言,它是一种用来确定XML文档中某部分位置的语言,同样适用于HTML文档的检索
2、示例HTML代码
<ul class="book_list">
<li>
<title class="book_001">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>69.99</price>
</li>
<li>
<title class="book_002">Spider</title>
<author>Forever</author>
<year>2019</year>
<price>49.99</price>
</li>
</ul>
3、匹配演示
1、查找所有的li节点
//li
2、查找li节点下的title子节点中,class属性值为'book_001'的节点
//li/title[@class="book_001"]
3、查找li节点下所有title节点的,class属性的值
//li//title/@class
# 只要涉及到条件,加 []
# 只要获取属性值,加 @
4、选取节点
1、// :从所有节点中查找(包括子节点和后代节点)
2、@ :获取属性值
# 使用场景1(属性值作为条件)
//div[@class="movie"]
# 使用场景2(直接获取属性值)
//div/a/@src
5、匹配多路径
xpath表达式1 | xpath表达式2 | xpath表达式3
6、常用函数
contains() :匹配属性值中包含某些字符串节点
# 查找class属性值中包含"book_"的title节点
//title[contains(@class,"book_")]
# 匹配所有段子的 div 节点
//div[contains(@id,"qiushi_tag_")]
<div class="article block untagged mb15 typs_long" id="qiushi_tag_122044339">
</div>
<div class="article block untagged mb15 typs_long" id="qiushi_tag_122044339">
</div>
text() :获取节点的文本内容
# 查找所有书籍的名称
//ul[@class="book_list"]/li/title
#结果:<element title at xxxx>
//ul[@class="book_list"]/li/title/text()
#结果:'Harry Potter'
1、获取猫眼电影中电影信息的 dd 节点
//dl[@class="board-wrapper"]/dd
2、获取电影名称的xpath://dl[@class="board-wrapper"]/dd//p[@class="name"]/a/text()
获取电影主演的xpath://dl[@class="board-wrapper"]/dd//p[@class="star"]/text()
获取上映商检的xpath://dl[@class="board-wrapper"]/dd//p[@class="releasetime"]/text()
7、匹配规则
节点对象列表
# xpath示例: //div、//div[@class="student"]、//div/a[@title="stu"]/span
字符串列表
# xpath表达式中末尾为: @src、@href、text()
8、xpath高级
-
基准xpath表达式: 得到节点对象列表
-
for r in [节点对象列表]:
username = r.xpath('./xxxxxx')
# 此处注意遍历后继续xpath一定要以: . 开头,代表当前节点
七、lxml解析库
1、使用流程
导模块
from lxml import etree
创建解析对象
parse_html = etree.HTML(html)
解析对象调用xpath
r_list = parse_html.xpath('xpath表达式')
# 只要调用xpath,结果一定为列表
2、练习
from lxml import etree
html = '''<div class="wrapper">
<i class="iconfont icon-back" id="back"></i>
<a href="/" id="channel">新浪社会</a>
<ul id="nav">
<li><a href="http://domestic.firefox.sina.com/" title="国内">国内</a></li>
<li><a href="http://world.firefox.sina.com/" title="国际">国际</a></li>
<li><a href="http://mil.firefox.sina.com/" title="军事">军事</a></li>
<li><a href="http://photo.firefox.sina.com/" title="图片">图片</a></li>
<li><a href="http://society.firefox.sina.com/" title="社会">社会</a></li>
<li><a href="http://ent.firefox.sina.com/" title="娱乐">娱乐</a></li>
<li><a href="http://tech.firefox.sina.com/" title="科技">科技</a></li>
<li><a href="http://sports.firefox.sina.com/" title="体育">体育</a></li>
<li><a href="http://finance.firefox.sina.com/" title="财经">财经</a></li>
<li><a href="http://auto.firefox.sina.com/" title="汽车">汽车</a></li>
</ul>
<i class="iconfont icon-liebiao" id="menu"></i>
</div>'''
# 获取所有 a 节点的文本内容
//ul//li/a/text()
# 获取所有 a 节点的 href 的属性值
//ul//li/a/@href
# 获取 图片、军事、...,不包括新浪社会
//ul//li/a/title[not(contains(@title,"社会"))]/text()