0. 什么是爬虫?
通过编写程序,模拟浏览器上网,然后让其抓取数据的过程。
1. 常用请求头和响应头信息
1.1 常用请求头信息
- User-Agent:请求载体的身份标识(如谷歌浏览器)。
- Connection:请求完毕后是断开连接还是保持连接。
1.2 常用响应头信息
- Content-Type:服务器响应回客户端的数据类型。
1.3 HTTPS协议
安全的HTTP协议。
加密方式:
- 对称密钥加密
- 非对称密钥加密
- 证书密钥加密(HTTPS)
2. Requests库
2.1 作用
模拟浏览器发请求的过程。
2.2 入门案例
爬取搜狗首页。
import requests
# 1. 指定url
url = "http://www.sogou.com"
# 2. 发起请求
resp = requests.get(url=url)
# 3. 获取响应数据
print(type(resp.text))
''' <class 'str'> '''
print(resp.text)
''' (HTML文本) '''
# 4. 持久化存储
with open("./sogou.html", "w", encoding="utf-8") as fp:
fp.write(resp.text)
2.3 UA伪装
爬取搜狗某个关键字的搜索结果。
import requests
# 1. 指定url
# url格式:https://www.sogou.com/web?query=%E4%BD%A0%E5%A6%88
url = "https://www.sogou.com/web"
params = {
"query": "苏州大学",
}
# 2. UA伪装
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.52",
}
# 3. 发起请求(带参数)
resp = requests.get(url=url, params=params, headers=headers)
# 4. 获取响应数据并持久化存储
with open("result.html", "w", encoding="utf-8") as fp:
fp.write(resp.text)
2.4 带参数的URL
见上一节。
2.5 Ajax请求
爬取百度翻译的翻译结果。
2.5.1 在浏览器中抓取Ajax请求
2.5.2 分析请求和响应信息
2.5.3 代码实现
import requests
import json
# 1. 指定url
url = "https://fanyi.baidu.com/sug"
# 2. POST请求参数处理
data = {
"kw": "python",
}
# 3. UA伪装
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.52",
}
# 4. 发起请求
resp = requests.post(url=url, data=data, headers=headers)
# 5. 获取响应数据(json)
json_data = resp.json()
print(json_data)
''' {'errno': 0, 'data': [{'k': 'python', 'v': 'n. 蟒; 蚺蛇;'}, {'k': 'pythons', 'v': 'n. 蟒; 蚺蛇; python的复数;'}]} '''
# 6. 持久化存储
with open("result.json", "w", encoding="utf-8") as fp:
json.dump(json_data, fp)
2.6 下载图片(二进制文件)
import requests
# 1. 指定url
url = "https://pic.qiushibaike.com/system/pictures/12343/123433317/medium/UURMN5VO96YT2MZX.jpg"
# 2. UA伪装
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.52",
}
# 3. 发起请求
resp = requests.get(url=url, headers=headers)
# 4. 获取响应数据(二进制)
img_data = resp.content
print(type(img_data))
''' <class 'bytes'> '''
# 5. 持久化存储
with open("./img.jpg", "wb") as fp:
fp.write(img_data)
2.7 综合应用
爬取豆瓣电影分类排行榜TOP100。
2.7.1 分析Ajax请求和响应信息
打开豆瓣电影分类排行榜,按F12开始抓取。
2.7.2 代码实现
import requests
# 1. 指定url
url = "https://movie.douban.com/j/chart/top_list"
params = {
"type": "24",
"interval_id": "100:90",
"action": "",
"start": "0", # 从库中第几部电影开始
"limit": "100", # 一次取出的数量
}
# 2. UA伪装
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.52",
}
# 3. 发起请求
resp = requests.get(url=url, params=params, headers=headers)
# 4. 获取响应数据
json_data = resp.json()
# 5. 提取信息
for item in json_data:
title = item.get("title")
score = item.get("score")
print(f"《{title}》\t评分:{score}")
3. 正则表达式
3.1 爬取图片
爬取糗事百科的图片。
3.1.1 分析HTML结构
可以看出,我们只需要处理class为“thumb”的div标签内的图片即可。
3.1.2 代码实现
import requests
import re
import os
# 1. 指定url
url = "https://www.qiushibaike.com/imgrank/"
# 2. UA伪装
headers = {
# "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.52",
}
# 3. 发起请求
resp = requests.get(url=url, headers=headers)
# 4. 获取响应数据
html = resp.text
# 5. 提取所有图片链接
pat = re.compile(r'<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>', re.S)
img_urls = pat.findall(html)
print(img_urls[:3])
''' ['//pic.qiushibaike.com/system/pictures/12329/123291166/medium/RUO963TBFGZH5VDK.jpg', '//pic.qiushibaike.com/system/pictures/12339/123394757/medium/4HAZDM2FIL6EOBY9.jpg', '//pic.qiushibaike.com/system/pictures/12342/123426805/medium/L197GOMSMVURGS8K.jpg'] '''
# 6. 下载所有图片
if not os.path.isdir("./img"):
os.mkdir("./img")
for idx, img_url in enumerate(img_urls):
assert img_url[:2] == "//"
img_url = "https:" + img_url
img_data = requests.get(url=img_url, headers=headers).content
with open(f"./img/{idx}.jpg", "wb") as fp:
fp.write(img_data)
4. BeautifulSoup
4.1 bs4数据解析的原理
- 实例化一个BeautifulSoup对象,将页面源码数据加载到该对象中
- 通过调用相应的方法,进行标签定位和数据提取
4.2 HTML演示代码
以下所有演示均是对该HTML代码进行的:
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<title>测试bs4</title>
</head>
<body>
<div>
<p>百里守约</p>
</div>
<div class="song">
<p>李清照</p>
<p>王安石</p>
<p>苏轼</p>
<p>柳宗元</p>
<a href="http://www.song.com/" title="赵匡胤" target="_self">
<span>this is span</span>
宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a>
<a href="" class="du">总为浮云能蔽日,长安不见使人愁</a>
<img src="http://www.baidu.com/meinv.jpg" alt=""/>
</div>
<div class="song">
我也是一个song标签
</div>
<div class="tang">
<ul>
<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
<li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
<li><a href="http://www.126.com" alt="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
<li><a href="http://www.sina.com" class="du">杜甫</a></li>
<li><a href="http://www.dudu.com" class="du">杜牧</a></li>
<li><b>杜小月</b></li>
<li><i>度蜜月</i></li>
<li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>
</ul>
</div>
</body>
</html>
4.3 获取标签
4.3.1 获取单个标签
from bs4 import BeautifulSoup
# 将本地HTML加载进BeautifulSoup对象
fp = open("in.html", "r", encoding="utf-8")
soup = BeautifulSoup(fp, "lxml")
# 1. 获取单个标签
# 1.1 获取文档中第一个<a>标签
tag_a = soup.a
print(type(tag_a))
''' <class 'bs4.element.Tag'> '''
print(tag_a)
'''
<a href="http://www.song.com/" target="_self" title="赵匡胤">
<span>this is span</span>
宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a> '''
# 1.2 find()方法作用相同
tag_a = soup.find("a")
print(tag_a)
# 2. 属性定位
# 2.1 获取class为song的第一个标签
tag_div = soup.find("div", class_="song")
print(tag_div)
# 3. 获取一类标签
# 3.1 获取所有<a>标签
tag_as = soup.find_all("a")
print(type(tag_as))
''' <class 'bs4.element.ResultSet'> '''
print(len(tag_as))
''' 8 '''
print(type(tag_as[5]))
''' <class 'bs4.element.Tag'> '''
# 4. 选择器
# 4.1 选择class为song的所有标签
tag_song = soup.select(".song")
print(type(tag_song))
''' <class 'bs4.element.ResultSet'> '''
print(len(tag_song))
''' 2 '''
print(type(tag_song[0]))
''' <class 'bs4.element.Tag'> '''
# 4.2 直系层级选择(选择tang中li下所有儿子a标签)
tag_as = soup.select(".tang > ul > li > a")
print(tag_as)
''' [<a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a>, <a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a>, <a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a>, <a class="du" href="http://www.sina.com">杜甫</a>, <a class="du" href="http://www.dudu.com">杜牧</a>, <a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a>] '''
tag_as = soup.select(".tang > ul > a")
print(tag_as)
''' [] '''
# 4.3 非直系层级选择(选择ul下所有子孙a标签)
tag_as = soup.select(".tang > ul a")
print(tag_as)
''' [<a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a>, <a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a>, <a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a>, <a class="du" href="http://www.sina.com">杜甫</a>, <a class="du" href="http://www.dudu.com">杜牧</a>, <a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a>] '''
4.3.2 属性定位
见上一节。
4.3.3 获取一类标签
见上一节。
4.3.4 选择器(最常用)
见上一节。
4.4 获取标签内的文本数据
形如获取<p>abc</p>中间的“abc”。
from bs4 import BeautifulSoup
# 将本地HTML加载进BeautifulSoup对象
fp = open("in.html", "r", encoding="utf-8")
soup = BeautifulSoup(fp, "lxml")
# 1. tag.text/tag.get_text():获取标签内所有文本
tag_div = soup.select(".tang")[0]
print(tag_div.text)
'''
清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村
秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山
岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君
杜甫
杜牧
杜小月
度蜜月
凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘
'''
# 2. tag.string:获取标签的直系文本
print(tag_div.string)
''' None '''
tag_a = soup.select(".song > a")[0]
print(tag_a.string)
''' None '''
tag_p = soup.select(".song > p")[0]
print(tag_p.string)
''' 李清照 '''
4.5 获取标签的属性值
from bs4 import BeautifulSoup
# 将本地HTML加载进BeautifulSoup对象
fp = open("in.html", "r", encoding="utf-8")
soup = BeautifulSoup(fp, "lxml")
# 1. 获取所有a标签的href属性值
tag_as = soup.select("a")
for tag_a in tag_as:
print(tag_a["href"])
'''
http://www.song.com/
http://www.baidu.com
http://www.163.com
http://www.126.com
http://www.sina.com
http://www.dudu.com
http://www.haha.com
'''
5. xpath
5.1 xpath解析原理
- 实例化一个etree的对象,并且需要将被解析的页面源码数据加载到该对象中。
- 调用etree对象中的xpath方法,结合着xpath表达式,实现标签定位和内容捕获。
5.2 HTML演示代码
以下所有演示均是对该HTML代码进行的:
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<title>测试bs4</title>
</head>
<body>
<div>
<p>百里守约</p>
</div>
<div class="song">
<p>李清照</p>
<p>王安石</p>
<p>苏轼</p>
<p>柳宗元</p>
<a href="http://www.song.com/" title="赵匡胤" target="_self">
<span>this is span</span>
宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a>
<a href="" class="du">总为浮云能蔽日,长安不见使人愁</a>
<img src="http://www.baidu.com/meinv.jpg" alt=""/>
</div>
<div class="song">
我也是一个song标签
</div>
<div class="tang">
<ul>
<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
<li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
<li><a href="http://www.126.com" alt="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
<li><a href="http://www.sina.com" class="du">杜甫</a></li>
<li><a href="http://www.dudu.com" class="du">杜牧</a></li>
<li><b>杜小月</b></li>
<li><i>度蜜月</i></li>
<li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>
</ul>
</div>
</body>
</html>
5.3 xpath语法
from lxml import etree
# 实例化etree对象
html = open("in.html", "r", encoding="utf-8").read()
tree = etree.HTML(html)
# 1. 从根结点开始定位“/”
div_tags = tree.xpath("/html/body/div")
print(type(div_tags)) # xpath()返回的永远是list
''' <class 'list'> '''
print(div_tags)
''' [<Element div at 0x22779bf9588>, <Element div at 0x22779bf9ac8>, <Element div at 0x22779bf9f08>, <Element div at 0x22779bfa0c8>] '''
# 2. 跨越层级“//”
# 2.1 /html下所有子孙div
div_tags = tree.xpath("/html//div")
print(div_tags)
''' [<Element div at 0x202e25d8e48>, <Element div at 0x202e25d8b88>, <Element div at 0x202e25d8ec8>, <Element div at 0x202e25d8f08>] '''
# 2.2 整个文档下所有子孙div
div_tags = tree.xpath("//div")
print(div_tags)
''' [<Element div at 0x202e25d8e48>, <Element div at 0x202e25d8b88>, <Element div at 0x202e25d8ec8>, <Element div at 0x202e25d8f08>] '''
# 3. 属性定位 & 索引定位
# 3.1 所有class为song的div标签
div_tags = tree.xpath('//div[@class="song"]')
print(div_tags)
''' [<Element div at 0x2699476a308>, <Element div at 0x2699476a388>] '''
# 3.2 满足条件的第3个p标签(索引从1开始)
div_tags = tree.xpath('//div[@class="song"]/p[3]')
print(div_tags)
''' [<Element p at 0x2699476adc8>] '''
# 4. 获取标签的文本 & 属性
# 4.1 获取a标签的文本(直系)
a_text = tree.xpath("//div[@class='tang']//li[5]/a/text()")
print(a_text)
''' ['杜牧'] '''
# 4.2 获取标签下所有子孙文本
li_text = tree.xpath("//li[7]/text()")
print(li_text)
''' [] '''
li_text = tree.xpath("//li[7]//text()")
print(li_text)
''' ['度蜜月'] '''
li_text = tree.xpath("//div[@class='tang']//text()")
print(li_text)
''' ['n ', '\n ', '清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村', '\n ', '秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山', '\n ', '岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君', '\n ', '杜甫', '\n ', '杜牧', '\n ', '杜小月', '\n ', '度蜜月', '\n ', '凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘', '\n ', '\n'] '''
# 4.3 获取标签的属性值
url = tree.xpath("//div[@class='song']/img/@src")
print(url)
''' ['http://www.baidu.com/meinv.jpg'] '''
注:对于每个获取到的Element对象,可以继续调用xpath()方法!但是要以“./”开始,表示相对路径。