少年意气强不羁,虎胁插翼白日飞。这篇文章主要讲述Python技能树共建requests-html库初识相关的知识,希望能为你提供帮助。
requests-html 模块是什么requests-html
模块安装使用 pip install requests-html
即可,官方手册查询地址:requests-html.kennethreitz.org/,官方并没有直接的中文翻译,在检索过程中,确实发现了一版中文手册,在文末提供。
先看一下官方对该库的基本描述:
- Full javascript support!(完全支持 JS,这里手册还重点标记了一下,初学阶段可以先忽略)
- CSS Selectors (a.k.a jQuery-style, thanks to PyQuery).(集成了 pyquery 库,支持 css 选择器)
- XPath Selectors, for the faint at heart.(支持 XPath 选择器)
- Mocked user-agent (like a real web browser).(mock UA 数据,这点不错)
- Automatic following of redirects.(自动跟踪重定向)
- Connection–pooling and cookie persistence.(持久性 COOKIE)
- The Requests experience you know and love, with magical parsing abilities.(额,这最后一点,各位自己领悟吧)
对于该库的简单使用,代码如下所示:
from requests_html import HTMLSession
session = HTMLSession()r = session.get(https://python.org/)print(r)
首先从
requests_html
库导入 HTMLSession
类,然后将其实例化之后,调用其 get
方法,发送请求,得到的 r
输出为 &
lt;
Response [200]&
gt;
,后续即可使用内置的解析库对数据进行解析。由于该库是解析
html
对象,所以可以查看对应的 html
对象包含哪些方法与与属性。通过
dir
函数查阅。print(dir(r.html))
# 输出如下内容:
[__aiter__, __anext__, __class__, __delattr__, __dict__, __dir__, __doc__, __eq__, __format__, __ge__,
__getattribute__, __gt__, __hash__, __init__, __init_subclass__, __iter__, __le__, __lt__, __module__, __ne__,
__new__, __next__, __reduce__, __reduce_ex__, __repr__, __setattr__, __sizeof__, __str__, __subclasshook__,
__weakref__, _async_render, _encoding, _html, _lxml, _make_absolute, _pq, absolute_links, add_next_symbol,
arender, base_url, default_encoding, element, encoding, find, full_text, html, links, lxml, next,
next_symbol, page, pq, raw_html, render, search, search_all, session, skip_anchors, text, url, xpath]
该函数只能输入大概内容,细节还是需要通过 help 函数查询,例如:
html 对象的方法包括
find
:提供一个 css 选择器,返回一个元素列表;xpath
:提供一个 xpath 表达式,返回一个元素列表;search
: 根据传入的模板参数,查找 Element 对象;search_all
:同上,返回的全部数据;
links
:返回页面所有链接;absolute_links
:返回页面所有链接的绝对地址;base_url
:页面的基准 URL;html
,raw_html
,text
:以 HTML 格式输入页面,输出未解析过的网页,提取页面所有文本;
requests-html
库将通过 3~4 个案例进行学习掌握,接下来进入第一个案例。怎么用 目标站点分析
本次要采集的目标网站为:
www.world68.com/top.asp?t=5star&
page=1
,目标站点描述为【全球名站】。文章图片
在获取数据源发送请求前,忽然想起可以动态修改
user-agent
,查阅该库源码发现,它只是使用了 fake_useragent
库来进行操作,并无太神奇的地方,所以可用可不用该内容。DEFAULT_USER_AGENT = Mozilla/5.0 (Macintosh;
Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8def user_agent(style=None) ->
_UserAgent:
"""Returns an apparently legit user-agent, if not requested one of a specific
style. Defaults to a Chrome-style User-Agent.
"""
global useragent
if (not useragent) and style:
useragent = UserAgent()return useragent[style] if style else DEFAULT_USER_AGENT
其余内容相对比较简单,页码规则如下:
www.world68.com/top.asp?t=5star&
page=1
www.world68.com/top.asp?t=5star&
page=2
累计页数直接在底部进行了展示,可以设计为用户手动输入,即
input
函数实现。目标数据存储网站名与网站地址即可,基于此,开始编码。
编码时间
首先通过单线程实现
requests-html
的基本逻辑,注意到下述代码非常轻量,from requests_html import HTMLSessionsession = HTMLSession()page_size = int(input("请输入总页码:"))
for page in range(1, page_size + 1):world = session.get(fhttp://www.world68.com/top.asp?t=5star&
page=page)
world.encoding = gb2312
# world.html.encoding = "gb2312"
# print(world.text)
print("正在采集数据", world.url)
title_a = world.html.find(dl>
dt>
a)
for item in title_a:
name = item.text
url = item.attrs[href]
with open(webs.txt, "a+", encoding="utf-8") as f:
f.write(f"name,url\\n")
上述代码重点部分说明如下:
world.encoding
,设置了网页解析编码;world.html.find(dl& gt; dt& gt; a)
通过 css 选择器,查找所有的网页标题元素;item.text
提取网页标题内容;item.attrs[href]
获取元素属性,即网站域名。
3519
个站点,就不在提供了,简单运行 1 分钟代码,即可得到。![requests-html库初识 + 无资料解BUG之 I/O error : encoder error,Python爬虫第30例](https://img-blog.csdnimg.cn/2f8a17b9d45b426487675ab2f69dfc67.png =400x)
由于上述代码太少了,完全不够今日代码量,我们顺手将其修改为多线程形式。
import requests_html
import threading
import time
import fcntlclass MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)def run(self):
global page, lock, page_size
while True:
lock.acquire(True)
if page >
= page_size:
lock.release()
break
else:
page += 1
lock.release()
requests_html.DEFAULT_ENCODING = "gb18030"
session = requests_html.HTMLSession()print("正在采集第页".format(page), "*" * 50)
try:
page_url = fhttp://www.world68.com/top.asp?t=5star&
page=page
world = session.get(page_url, timeout=10)
print("正在采集数据", world.url)
# print(world.html)
title_a = world.html.find(dl>
dt>
a)
print(title_a)
my_str = ""for item in title_a:
name = item.text
url = item.attrs[href]
my_str += f"name.encode(utf-8).decode(utf-8),url\\n"with open(thread_webs.txt, "a+", encoding="utf-8") as f:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)# 文件加锁
f.write(f"my_str")except Exception as e:
print(e, page_url)if "__main__" == __name__:
page_size = int(input("请输入总页码:"))
page = 0
thread_list = []# 获取开始时间
start = time.perf_counter()lock = threading.Lock()
for i in range(1, 5):
t = MyThread()
thread_list.append(t)
for t in thread_list:
t.start()
for t in thread_list:
t.join()
# 获取时间间隔
elapsed = (time.perf_counter() - start)
print("程序运行完毕,总耗时为:", elapsed)
在正式进行编码之后,发现存在比较大的问题,编码问题,出现如下错误:
encoding error : input conversion failed due to input error, bytes 0x81 0xE3 0xD3 0xAA
encoding error : input conversion failed due to input error, bytes 0x81 0xE3 0xD3 0xAA
encoding error : input conversion failed due to input error, bytes 0x81 0xE3 0xD3 0xAA
I/O error : encoder error
该错误在执行单线程时并未发生,但是当执行多线程时,异常开始出现,本问题在互联网上无解决方案,只能自行通过
requests-html
库的源码进行修改。打开
requests_html.py
文件,将 417 行左右的代码进行如下修改:def __init__(self, *, session: Union[HTMLSession, AsyncHTMLSession] = None, url: str = DEFAULT_URL, html: _HTML, default_encoding: str = DEFAULT_ENCODING, async_: bool = False) ->
None:
# 修改本部分代码
# Convert incoming unicode HTML into bytes.
# if isinstance(html, str):
html = html.decode(DEFAULT_ENCODING,replace)super(HTML, self).__init__(
# Convert unicode HTML to bytes.
element=PyQuery(html)(html) or PyQuery(f<
html>
html<
/html>
)(html),
html=html,
url=url,
default_encoding=default_encoding
)
代码
if isinstance(html, str):
用于判断 html
是否为 str
,但是在实测过程中发现 html
是 &
lt;
class bytes&
gt;
类型,所以数据没有进行转码工作,故取消相关判断。除此以外,通过输出
world.html.encoding
发现网页的编码不是 GB2312
,而是 gb18030
,所以通过下述代码进行了默认编码的设置。requests_html.DEFAULT_ENCODING = "gb18030"
【Python技能树共建requests-html库初识】按照如上内容进行修改之后,代码可以正常运行,数据能正确的采集到。
本案例还新增了代码运行时长的计算,具体如下:
# 获取开始时间
start = time.perf_counter()
# 执行代码的部分
# 获取时间间隔
elapsed = (time.perf_counter() - start)
print("程序运行完毕,总耗时为:", elapsed)
完整的代码运行效果如下所示:
文章图片
推荐阅读
- SAP Marketing Cloud Contact 模型的导入配置和数据合并原理
- POJ - 1062nyoj - 510昂贵的聘礼 (Dijkstra最短路+思维)
- 51Nod - 1001数组中和等于K的数对 (排序+ 尺取)
- HDU - 5605 geometry(水,数学题,推公式)
- HDU - 3790最短路径问题(DIjkstra算法双权值)
- HDU - 3342Legal or Not(拓扑排序)
- HDU - 3499 Flight (单源最短路+优惠问题)
- POJ-3259 Wormholes(判负环,spfa算法)
- 树莓派开发笔记(十七)(树莓派4B+上Qt多用户连接操作Mysql数据库同步(单条数据悲观锁))