文章目录
之前说的都是通用爬虫,只能爬取整张页面的数据,而对于细枝末节却无能为力,不能爬页面中某一个区域的数据。
举个例子:
这是上次获得的企业数据,但我只想要文字内容,去掉看不懂的英文,这该怎么做呢?
为了解决这一问题,今天又要介绍另一种爬虫类别了 —— 聚焦爬虫。
(小声bb,这需要HTML基础)
聚焦爬虫:
建立在通用爬虫的基础之上,抓取页面中指定的局部内容。
说到聚焦爬虫,就不得不提到实现聚焦爬虫的方法—数据解析。
数据解析分类:
——正则
——bs4
——xpath(通用性强,其他语言和之后的框架都能用)
数据解析原理:
想要的局部文本内容都会在标签之间或者标签对应的属性中进行存储。
具体步骤:
- 进行指定标签的定位(标签定位)
- 提取(解析)标签、标签属性中存储的数据值
聚焦爬虫编码流程:
- 指定 url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
我主要用的是xpath进行数据解析。
xpath解析:最常用且最便捷高效的一种解析方式
解析原理:
- 实例化一个叫 etree 的对象,且需要将被解析的页面源码数据加载到该对象中。
- 调用 etree 对象中的 xpath 方法结合着 xpath 表达式,实现标签的定位和内容的捕获。
环境安装:
pip install lxml
如何实例化一个 etree 对象: ⭐重点!!!
(先导包:from lxml import etree)
分为两种情况↓
-
将本地的 html 文档中的源码数据加载到 etree 对象中
对象 = etree.pares(‘文件路径’) -
将互联网上获得的源码数据加载到该对象中
对象 = etree.HTML(‘page_text’)
下面的👇标签的定位和内容的捕获 ⭐重点!!!
标签的定位定位:
对象.xpath(‘xpath表达式’)
重要的是如何写 xpath 表达式!!!
-
标签定位:
/:
写在开头时,表示从根节点开始定位
r = tree.xpath(‘/html/body/div’)
写在中间表示的是一个层级
//:
写在开头时表示从任意位置开始定位
r = tree.xpath(‘//div’)
写在中间表示多个层级
r = tree.xpath(‘/html//div’)
上面👆这三种写法意思相同 -
属性定位:
标签[@属性名 = ‘值’]
div[@class = ‘handsome’] -
索引定位:
属性定位 / 标签 [序号]
例:选取属性为handsome的 div 下的第3个<p>标签
tree.xpath(‘//div[@class = ‘handsome’]/p[3]’)
⭐注意:索引从1开始!!!⭐
当定位到想找的标签后,该取我们想要的内容了。
内容的捕获:
标签外的叫文本,标签里的叫属性
- 取文本:
取标签下的文本内容 <a>文本内容</a>
/text() :
获取标签中直系的文本内容
xpath(‘//div[@class = ‘handsome’]/li[7]/i/text()’) 属性为handsome的div下第7个<li>标签里的<i>标签中的文本内容
//text() :
标签中非直系的文本内容(所有文本内容)
tree.xpath(‘//div[@class = ‘handsome’]//li/text()’) 属性为handsome的div下所有<li>标签的文本内容
tree.xpath(‘//div[@class = ‘handsome’]//text()’) 属性为handsome的div下所有文本内容 - 取属性:
取标签里的属性 <a>文本内容</a>
/@属性名
例:
<div class = ‘handsome’>
<img src = ‘https:网址’/>
我们想取<img>标签中的 src属性 对应的网址,tree.xpath(‘//div[@class = ‘handsome’]/img/@src’)
中文乱码问题:
原因:原始页面编码方式和编译器中编码方式冲突
中文乱码问题解决方法:
- 手动设置响应数据的编码格式:(⭐推荐)
response.encoding = "gbk"
这里看页面编码方式具体是什么,是什么就写什么。
- 通用处理中文乱码的解决方案
存储中文的变量.encode('iso-8859-1').decode('gbk')
response.encoding = response.apparent_encoding
这样中文乱码问题就得以解决了。
补充:
xpath中的或( | ):
若想求2个不同路径但内容一样的数据(这两个数据得放在一起),
可以 tree.xpath(‘路径一 | 路径二’)
实战:
1. 爬取58二手房信息
https://xx.58.com/ershoufang/
import requests
from lxml import etree
# 这个网站被爬多了,弄了反爬机制,每次想爬需要点进去网站验证才能继续爬
url = 'https://xx.58.com/ershoufang/?utm_source=market&spm=u-2d2yxv86y3v43nkddh1.BDPCPZ_BT&PGTID=0d100000-003f-86a0' \
'-4e64-ee24ae2c52b1&ClickID=2 '
head = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 '
'Safari/537.36 SLBrowser/7.0.0.12151 SLBChan/30 '
}
response = requests.get(url, headers=head)
page_text = response.text
tree = etree.HTML(page_text)
r = tree.xpath('//div[@class = "property-content-title"]/h3/text()')
with open('58二手房.txt', 'w', encoding='utf-8') as f:
for title in r:
f.write(title + '\n')
还是老方法,发起请求,获取数据。不同的是,这次需要加上数据解析了,而数据解析的关键就是如何写出xpath表达式。
打开开发者工具(F12),选中想要获取的数据
先来个简单的,只爬取二手房房名。
通过观察发现,房名信息在属性值为"property-content-title"的<div>标签下的<h3>标签中
属性里和标签间的文本中都有房名信息,所以取文本 /text() 和取属性 /@属性名 都可以
r = tree.xpath('//div[@class = "property-content-title"]/h3/text()')
下面这个文本文件就是爬取到的结果👇
2. 爬取全国城市名称
先找到一个包含全国城市的网站
https://www.aqistudy.cn/historydata/
import requests
from lxml import etree
url = 'https://www.aqistudy.cn/historydata/'
head = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 '
'Safari/537.36 SLBrowser/7.0.0.12151 SLBChan/30 '
}
page_text = requests.get(url, headers=head).text
tree = etree.HTML(page_text)
li_list = tree.xpath('//ul[@class="unstyled"]/div[2]/li')
name_list = []
num = 0
for li in li_list:
name_list.append(li.xpath('./a/text()')[0])
num += 1
print(name_list)
print('共有%d个城市' % num)
直接看如何定位标签👇
可以直观看出想要的城市名位于属性名为"unstyled"的<ul>标签下的第2个<div>标签下的每个<li>标签中的<a>标签 (层层套娃)
li_list = tree.xpath('//ul[@class="unstyled"]/div[2]/li')
这时就取出了所有<li>标签组成的列表,想要得到每一个就需要循环遍历。
接着,我们要取每个<li>标签下的<a>标签,这些<a>标签位置需要衔接住上一个<li>标签
xpath表达式就可以这样写
.xpath('./a/text()')
./表示当前<li>位置
name_list = []
for li in li_list:
name_list.append(li.xpath('./a/text()'))
这样就实现了取每个<a>标签文本的目的。
但要是这么写,输出不太好看
每个城市名都在一个列表中,所以取文本时可以直接把它从列表中取出来,在后面加个[0]
就是开头写的那样。
name_list.append(li.xpath('./a/text()')[0])
3. 4k图片下载
https://pic.netbian.com/4kqiche/
import requests
from lxml import etree
import os
url = 'https://pic.netbian.com/4kqiche/'
head = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 '
'Safari/537.36 SLBrowser/7.0.0.12151 SLBChan/30 '
}
if not os.path.exists(r'C:\Users\myself\Desktop\Car'):
os.mkdir(r'C:\Users\myself\Desktop\Car')
response = requests.get(url, headers=head)
response.encoding = "gbk" # 网页的编码方式是gbk,为了使编译器和网页编码方式一致,将响应数据改成gbk格式,否则 img_name 里的中文会是乱码
page_text = response.text
tree = etree.HTML(page_text)
li_list = tree.xpath('//ul[@class="clearfix"]/li')
num = 1
for li in li_list:
img_src = li.xpath('./a/img/@src')[0]
img_url = 'https://pic.netbian.com/' + img_src # 图片网址,如https://pic.netbian.com//uploads/allimg/220308/005259-1646671979b62a.jpg
img_name = li.xpath('./a/img/@alt')[0] + '.jpg' # 图片名字,如rolls royce phantom orchid劳斯莱斯幻影兰花4k汽车壁纸.jpg
print(img_name, img_url)
img_data_b = requests.get(img_url, headers=head).content # 爬取图片数据用response.content,且数据是二进制形式,下面存储用wb
with open(r'C:\Users\myself\Desktop\Car\%s' % img_name, 'wb') as f:
f.write(img_data_b)
print(img_name, img_url)
print('第%d张图片下载完成' % num)
num += 1
在此补充一个知识点 , 爬取图片数据:
response.content
content返回的是二进制形式的图片数据
所以注意一点,之后打开文件要以二进制 wb 形式打开:
with open(‘图片名.jpg’,‘wb’) as f:
PS: wb = write byte,因为没有汉字,所以open()中不用写 utf-8
最后一个爬虫就不讲解了,之前的文章都有提到,看完我这三篇文章,大家就都能做一些初级爬虫项目了。