开源精选》是我们分享Github、Gitee等开源社区中优质项目的栏目,包括技术、学习、实用与各种有趣的内容。本期推荐的是一个开源基于 python 的 Web 自动化操作集成工具——DrissionPage。
用 requests 做数据采集面对要登录的网站时,要分析数据包、JS 源码,构造复杂的请求,往往还要应付验证码、JS 混淆、签名参数等反爬手段,门槛较高。若数据是由 JS 计算生成的,还须重现计算过程,体验不好,开发效率不高。
使用 selenium,可以很大程度上绕过这些坑,但 selenium 效率不高。因此,这个库将 selenium 和 requests 合而为一,不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率。
除了合并两者,本库还以网页为单位封装了常用功能,简化了 selenium 的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。一切从简,尽量提供简单直接的使用方法,对新手更友好。
如图所示,Drission对象负责链接的创建、共享登录状态等工作,类似 selenium 中 driver 的概念。MixPage对象负责对获取到的页面进行解析、操作。DriverElement和SessionElement则是从页面对象中获取到的元素对象。负责对元素进行解析和操作。
与 selenium 代码对比
跳转到第一个标签页
# 使用 selenium:
driver.switch_to.window(driver.window_handles[0])
# 使用 DrissionPage:
page.to_tab(0)按文本选择下拉列表
# 使用 selenium:
from selenium.webdriver.support.select import Select
select_element = Select(element)
select_element.select_by_visible_text('text')
# 使用 DrissionPage:
element.select('text')拖拽一个元素
# 使用 selenium:
ActionChains(driver).drag_and_drop(ele1, ele2).perform()
# 使用 DrissionPage:
ele1.drag_to(ele2)与 requests 代码对比
获取元素内容
url = 'https://baike.baidu.com/item/python'
# 使用 requests:
from lxml import etree
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36'}
response = requests.get(url, headers=headers)
html = etree.HTML(response.text)
element = html.xpath('//h1')[0]
title = element.text
# 使用 DrissionPage:
page = MixPage('s')
page.get(url)
title = page('tag:h1').text下载文件
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
save_path = r'C:\download'
# 使用 requests:
r = requests.get(url)
with open(f'{save_path}\\img.png', 'wb') as fd:
for chunk in r.iter_content():
fd.write(chunk)
# 使用 DrissionPage:
page.download(url, save_path, 'img') # 支持重命名,处理文件名冲突,自动创建目标文件夹爬取新冠排行榜
网址:https://www.outbreak.my/zh/world,此示例爬取全球新冠情况排行榜。该网站是纯 html 页面,特别适合 s 模式爬取和解析。
from DrissionPage import MixPage
# 用 s 模式创建页面对象
page = MixPage('s')
# 访问数据网页
page.get('https://www.outbreak.my/zh/world')
# 获取表头元素
thead = page('tag:thead')
# 获取表头列,跳过其中的隐藏的列
title = thead.eles('tag:th@@-style:display: none;')
data = [th.text for th in title]
print(data) # 打印表头
# 获取内容表格元素
tbody = page('tag:tbody')
# 获取表格所有行
rows = tbody.eles('tag:tr')
for row in rows:
# 获取当前行所有列
cols = row.eles('tag:td')
# 生成当前行数据列表(跳过其中没用的几列)
data = [td.text for k, td in enumerate(cols) if k not in (2, 4, 6)]
print(data) # 打印行数据输出:
['总 (205)', '累积确诊', '死亡', '治愈', '现有确诊', '死亡率', '恢复率']
['美国', '55252823', '845745', '41467660', '12,939,418', '1.53%', '75.05%']
['印度', '34838804', '481080', '34266363', '91,361', '1.38%', '98.36%']
['巴西', '22277239', '619024', '21567845', '90,370', '2.78%', '96.82%']
['英国', '12748050', '148421', '10271706', '2,327,923', '1.16%', '80.57%']
['俄罗斯', '10499982', '308860', '9463919', '727,203', '2.94%', '90.13%']
['法国', '9740600', '123552', '8037752', '1,579,296', '1.27%', '82.52%']
......
登录 gitee 网站
网址:https://gitee.com/login,此示例演示使用控制浏览器的方式自动登录 gitee 网站。
from DrissionPage import MixPage
# 用 d 模式创建页面对象(默认模式)
page = MixPage()
# 跳转到登录页面
page.get('https://gitee.com/login')
# 定位到账号文本框并输入账号
page.ele('#user_login').input('你的账号')
# 定位到密码文本框并输入密码
page.ele('#user_password').input('你的密码')
# 点击登录按钮
page.ele('@value=登 录').click()—END—
开源协议:BSD-3-Clause
开源地址: https://gitee.com/g1879/DrissionPage
查看到直接是有图片的链接的,但是这只是当前的页面,继续往下翻,发现有些图片是懒加载的,并没有图片的链接。对于未浏览到的内容,其提供的图片是空白的图片链接。
但是,他们都提供了图片详情页的链接。
所以这里有两个思路,一个是解决懒加载的问题,另外一个是获取图片详情页,进入图片详情页中再下载图片。这里我们选择第二种,至此,思路就清晰了。
请求要爬取的页面→获取该页面所有的图片详情页链接→请求详情页,获取具体的图片链接→请求图片具体链接,获取图片二进制内容,保存到本地
利用代码实现:
from curl_cffi import requests
from lxml import etree
url = "https://pixabay.com/zh/illustrations/search/%e8%87%aa%e7%84%b6/"
headers = {
'User-Agent':'Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0',
}
# 第一次请求,获取该页面所有的图片详情页链接
req = requests.get(url,headers=headers,impersonate='chrome')
html_tree = etree.HTML(req.text)
pic_srclist = html_tree.xpath('//a[@class="link--WHWzm"]/@href')
# print(pic_srclist)
print(req.status_code)
i = 0
for pic_src in pic_srclist:
i = i + 1
picpage_url = "https://pixabay.com"+pic_src
print('正在获取',i)
#第二次请求,获取图片详情页中的图片链接
temp_req = requests.get(url = picpage_url,headers=headers,impersonate='chrome')
temp_tree = etree.HTML(temp_req.text)
pic_url = temp_tree.xpath('//div[@class="container--3Mtk4"]//img/@src')[0]
pic_alt = temp_tree.xpath('//div[@class="container--3Mtk4"]//img/@alt')[0]
pic_name = pic_url.split('/')[-1]
# 第三次请求,获取图片具体链接
temp_req = requests.get(url = pic_url,headers=headers,impersonate='chrome')
with open(pic_name,'wb') as f:
f.write(temp_req.content)
print(pic_name,'获取完成!')还在为看小说受到广告影响而烦恼吗?不如利用起时间,跟着本文一起入门python爬虫,实现小说自由!
本项目的全部内容包括:
1.搜索功能实现
2.根据所选小说爬取全部章节列表
3.在线阅读所选章节
在一般的无反爬虫情形下,python请求网页信息的请求头较为简单,只写User-Agent项即可,可在检查中任意请求的请求头中查看
配置代码如下:
def __init__(self):
self._headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/98.0.4758.102 Safari/537.36 Edg/98.0.1108.55"
}
self._search_url = 'https://www.xbiquwx.la/modules/article/search.php?searchkey='
self._book_url = 'https://www.xbiquwx.la'
self.session = requests.session()上段代码中使用了session会话机制,可有效提升python的requests访问网站速度
在小说网站搜索万相之王,检查网络元素
发现其请求的url如上图中所示,其中的searchkey即为搜索文字的url编码
分析网页源码可知,我们所需的书名和链接均位于class属性值为grid的table标签中,使用xpath语法即可提取标签内容和标签属性
综上,这一部分输入书名搜索返回结果的代码如下:
def search(self, name:str):
if name:
response = self.session.get(
url = self._search_url + name,
headers = self._headers
)
html = etree.HTML(response.content.decode())
self.bookID = html.xpath('//table[@class="grid"]/tr/td[@class="odd"]/a/@href')
bookNameTag = html.xpath('//table[@class="grid"]/tr/td[@class="odd"]/a')
self.bookName = [item.text for item in bookNameTag]
return self.bookName打开任意一本书籍,检查加载书籍章节时的网络访问情况
可以发现请求的地址就是我们上一步中得到的书籍url,进一步证实了想法
在这里我们发现所有章节名称和url信息全部位于网页源码的dd标签中,同上,利用xpath语法提取
这一部分获取章节名字的代码如下:
def chapLists(self, index:int):
response = self.session.get(
url = self._book_url + self.bookID[index],
headers = self._headers
)
self.index = index
html = etree.HTML(response.content.decode())
self.chapUrls = html.xpath('//div[@class="box_con"]/div[@id="list"]//a/@href')
self.chapTitles = html.xpath('//div[@class="box_con"]/div[@id="list"]//a/@title')
self.chapUrls.reverse()
self.chapTitles.reverse()
return self.chapTitles我们打开任意一章小说,审查网络元素
同样可以发现所请求的网站就是上一步中得到的章节地址
在网页源码中我们发现小说正文部分位于id属性为content的div标签中,编写代码提取正文内容:
def read(self, index:int):
response = self.session.get(
url = self._book_url + self.bookID[self.index] + self.chapUrls[index],
headers = self._headers
)
code = response.apparent_encoding
html = etree.HTML(response.content.decode())
articleRaw = html.xpath('//div[@class="content_read"]//div[@id="content"]')
a0 = etree.tostring(articleRaw[0],encoding=code).decode(code)
a2 = a0[32:-29]
article = "<h1>"+self.chapTitles[index]+"</h1>"+'<font size="5"><br />'+a2+"</font>"
return article到此,我们已经掌握了整个小说阅读的来龙去脉
整个网络小说在线阅读类封装如下:
import requests
from lxml import etree
class bqCrawler():
def __init__(self):
self._headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/98.0.4758.102 Safari/537.36 Edg/98.0.1108.55"
}
self._search_url = 'https://www.xbiquwx.la/modules/article/search.php?searchkey='
self._book_url = 'https://www.xbiquwx.la'
self.session = requests.session()
def search(self, name:str):
if name:
response = self.session.get(
url = self._search_url + name,
headers = self._headers
)
html = etree.HTML(response.content.decode())
self.bookID = html.xpath('//table[@class="grid"]/tr/td[@class="odd"]/a/@href')
bookNameTag = html.xpath('//table[@class="grid"]/tr/td[@class="odd"]/a')
self.bookName = [item.text for item in bookNameTag]
return self.bookName
def chapLists(self, index:int):
response = self.session.get(
url = self._book_url + self.bookID[index],
headers = self._headers
)
self.index = index
html = etree.HTML(response.content.decode())
self.chapUrls = html.xpath('//div[@class="box_con"]/div[@id="list"]//a/@href')
self.chapTitles = html.xpath('//div[@class="box_con"]/div[@id="list"]//a/@title')
self.chapUrls.reverse()
self.chapTitles.reverse()
return self.chapTitles
def read(self, index:int):
response = self.session.get(
url = self._book_url + self.bookID[self.index] + self.chapUrls[index],
headers = self._headers
)
code = response.apparent_encoding
html = etree.HTML(response.content.decode())
articleRaw = html.xpath('//div[@class="content_read"]//div[@id="content"]')
a0 = etree.tostring(articleRaw[0],encoding=code).decode(code)
a2 = a0[32:-29]
article = "<h1>"+self.chapTitles[index]+"</h1>"+'<font size="5"><br />'+a2+"</font>"
return article在此基础上我们稍作修饰,用PyQt5为其开发一个可视化面板,最终效果如图:
*请认真填写需求信息,我们会在24小时内与您取得联系。