为了解决这一问题,今天又要介绍另一种爬虫类别了 —— 聚焦爬虫。
(小声bb,这需要HTML基础)
建立在通用爬虫的基础之上,抓取页面中指定的局部内容。
说到聚焦爬虫,就不得不提到实现聚焦爬虫的方法—数据解析。
——正则
——bs4
——xpath(通用性强,其他语言和之后的框架都能用)
想要的局部文本内容都会在标签之间或者标签对应的属性中进行存储。
具体步骤:
我主要用的是xpath进行数据解析。
- 实例化一个叫 etree 的对象,且需要将被解析的页面源码数据加载到该对象中。
- 调用 etree 对象中的 xpath 方法结合着 xpath 表达式,实现标签的定位和内容的捕获。
pip install lxml
(先导包: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个
标签
tree.xpath(‘//div[@class = ‘handsome’]/p[3]’)
⭐注意:索引从1开始!!!⭐
当定位到想找的标签后,该取我们想要的内容了。
标签外的叫文本,标签里的叫属性
response.encoding = "gbk"
这里看页面编码方式具体是什么,是什么就写什么。
存储中文的变量.encode('iso-8859-1').decode('gbk')
response.encoding = response.apparent_encoding
这样中文乱码问题就得以解决了。
xpath中的或( | ):
若想求2个不同路径但内容一样的数据(这两个数据得放在一起),
可以 tree.xpath(‘路径一 | 路径二’)
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"的
属性里和标签间的文本中都有房名信息,所以取文本 /text() 和取属性 /@属性名 都可以
r = tree.xpath('//div[@class = "property-content-title"]/h3/text()')
先找到一个包含全国城市的网站
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"的
li_list = tree.xpath('//ul[@class="unstyled"]/div[2]/li')
这时就取出了所有
.xpath('./a/text()')
./表示当前
name_list = []
for li in li_list:
name_list.append(li.xpath('./a/text()'))
这样就实现了取每个标签文本的目的。
但要是这么写,输出不太好看
每个城市名都在一个列表中,所以取文本时可以直接把它从列表中取出来,在后面加个[0]
就是开头写的那样。
name_list.append(li.xpath('./a/text()')[0])
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
最后一个爬虫就不讲解了,之前的文章都有提到,看完我这三篇文章,大家就都能做一些初级爬虫项目了。