Lmmso-Loser 官方论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 80|回复: 0

【Python技能树共建】requests-html库初识

[复制链接]
  • TA的每日心情
    无聊
    昨天 18:58
  • 签到天数: 22 天

    [LV.4]偶尔看看III

    177

    主题

    22

    回帖

    1541

    积分

    管理员

    积分
    1541
    发表于 2023-1-19 08:03:12 | 显示全部楼层 |阅读模式 来自 重庆大足县
    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.(额,这末了一点,各位本身意会吧)
    Only Python 3.6 is supported. 仅支持 Python 3.6 ,实测发现 3.6 以上版本仍旧可以。
    对于该库的简单使用,代码如下所示:
    1. from requests_html import HTMLSession
    2. session = HTMLSession()
    3. r = session.get('https://python.org/')
    4. print(r)
    复制代码
    首先从 requests_html 库导入 HTMLSession 类,然后将其实例化之后,调用其 get 方法,发送哀求,得到的 r 输出为 ,后续即可使用内置的分析库对数据举行分析。
    由于该库是分析 html 对象,以是可以查看对应的 html 对象包含哪些方法与与属性。
    通过 dir 函数查阅。
    1. print(dir(r.html))
    2. # 输出如下内容:
    3. ['__aiter__', '__anext__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
    4. '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__',
    5. '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
    6. '__weakref__', '_async_render', '_encoding', '_html', '_lxml', '_make_absolute', '_pq', 'absolute_links', 'add_next_symbol',
    7. 'arender', 'base_url', 'default_encoding', 'element', 'encoding', 'find', 'full_text', 'html', 'links', 'lxml', 'next',
    8. '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:同上,返回的全部数据;
    html 对象的属性包罗


    • links:返回页面全部链接;
    • absolute_links:返回页面全部链接的绝对地址;
    • base_url:页面的基准 URL;
    • html,raw_html,text:以 HTML 格式输入页面,输出未分析过的网页,提取页面全部文本;
    有了上述内容铺垫之后,在举行 Python 爬虫的编写就会变的轻易许多,requests-html 库将通过 3~4 个案例举行学习掌握,接下来进入第一个案例。
    怎么用

    目标站点分析

    本次要采集的目标网站为:www.world68.com/top.asp?t=5star&page=1,目标站点描述为【环球名站】。

    在获取数据源发送哀求前,忽然想起可以动态修改 user-agent,查阅该库源码发现,它只是使用了 fake_useragent 库来举行操纵,并无太神奇的地方,以是可用可不用该内容。
    1. 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.8'
    2. def user_agent(style=None) -> _UserAgent:
    3.     """Returns an apparently legit user-agent, if not requested one of a specific
    4.     style. Defaults to a Chrome-style User-Agent.
    5.     """
    6.     global useragent
    7.     if (not useragent) and style:
    8.         useragent = UserAgent()
    9.     return useragent[style] if style else DEFAULT_USER_AGENT
    复制代码
    其余内容相对比较简单,页码规则如下:
    1. www.world68.com/top.asp?t=5star&page=1
    2. www.world68.com/top.asp?t=5star&page=2
    复制代码
    累计页数直接在底部举行了展示,可以设计为用户手动输入,即 input 函数实现。
    目标数据存储网站名网站地址即可,基于此,开始编码。
    编码时间

    首先通过单线程实现 requests-html 的根本逻辑,注意到下述代码非常轻量,
    1. from requests_html import HTMLSession
    2. session = HTMLSession()
    3. page_size = int(input("请输入总页码:"))
    4. for page in range(1, page_size + 1):
    5.     world = session.get(f'http://www.world68.com/top.asp?t=5star&page={page}')
    6.     world.encoding = 'gb2312'
    7.     # world.html.encoding = "gb2312"
    8.     # print(world.text)
    9.     print("正在采集数据", world.url)
    10.     title_a = world.html.find('dl>dt>a')
    11.     for item in title_a:
    12.         name = item.text
    13.         url = item.attrs['href']
    14.         with open('webs.txt', "a+", encoding="utf-8") as f:
    15.             f.write(f"{name},{url}\n")
    复制代码
    上述代码重点部分说明如下:


    • world.encoding,设置了网页分析编码;
    • world.html.find('dl>dt>a') 通过 css 选择器,查找全部的网页标题元素;
    • item.text 提取网页标题内容;
    • item.attrs['href'] 获取元素属性,即网站域名。
    运行效果如下所示,获取到的 3519 个站点,就不在提供了,简单运行 1 分钟代码,即可得到。

    由于上述代码太少了,完全不够今日代码量,我们顺手将其修改为多线程情势。
    1. import requests_html
    2. import threading
    3. import time
    4. import fcntl
    5. class MyThread(threading.Thread):
    6.     def __init__(self):
    7.         threading.Thread.__init__(self)
    8.     def run(self):
    9.         global page, lock, page_size
    10.         while True:
    11.             lock.acquire(True)
    12.             if page >= page_size:
    13.                 lock.release()
    14.                 break
    15.             else:
    16.                 page += 1
    17.                 lock.release()
    18.                 requests_html.DEFAULT_ENCODING = "gb18030"
    19.                 session = requests_html.HTMLSession()
    20.                 print("正在采集第{}页".format(page), "*" * 50)
    21.                 try:
    22.                     page_url = f'http://www.world68.com/top.asp?t=5star&page={page}'
    23.                     world = session.get(page_url, timeout=10)
    24.                     print("正在采集数据", world.url)
    25.                     # print(world.html)
    26.                     title_a = world.html.find('dl>dt>a')
    27.                     print(title_a)
    28.                     my_str = ""
    29.                     for item in title_a:
    30.                         name = item.text
    31.                         url = item.attrs['href']
    32.                         my_str += f"{name.encode('utf-8').decode('utf-8')},{url}\n"
    33.                     with open('thread_webs.txt', "a+", encoding="utf-8") as f:
    34.                         fcntl.flock(f.fileno(), fcntl.LOCK_EX)  # 文件加锁
    35.                         f.write(f"{my_str}")
    36.                 except Exception as e:
    37.                     print(e, page_url)
    38. if "__main__" == __name__:
    39.     page_size = int(input("请输入总页码:"))
    40.     page = 0
    41.     thread_list = []
    42.     # 获取开始时间
    43.     start = time.perf_counter()
    44.     lock = threading.Lock()
    45.     for i in range(1, 5):
    46.         t = MyThread()
    47.         thread_list.append(t)
    48.     for t in thread_list:
    49.         t.start()
    50.     for t in thread_list:
    51.         t.join()
    52.     # 获取时间间隔
    53.     elapsed = (time.perf_counter() - start)
    54.     print("程序运行完毕,总耗时为:", elapsed)
    复制代码
    在正式举行编码之后,发现存在比较大的问题,编码问题,出现如下错误:
    1. encoding error : input conversion failed due to input error, bytes 0x81 0xE3 0xD3 0xAA
    2. encoding error : input conversion failed due to input error, bytes 0x81 0xE3 0xD3 0xAA
    3. encoding error : input conversion failed due to input error, bytes 0x81 0xE3 0xD3 0xAA
    4. I/O error : encoder error
    复制代码
    该错误在执行单线程时并未发生,但是当执行多线程时,非常开始出现,本问题在互联网上无办理方案,只能自行通过 requests-html 库的源码举行修改。
    打开 requests_html.py 文件,将 417 行左右的代码举行如下修改:
    1. def __init__(self, *, session: Union['HTMLSession', 'AsyncHTMLSession'] = None, url: str = DEFAULT_URL, html: _HTML, default_encoding: str = DEFAULT_ENCODING, async_: bool = False) -> None:
    2.         # 修改本部分代码
    3.     # Convert incoming unicode HTML into bytes.
    4.     # if isinstance(html, str):
    5.     html = html.decode(DEFAULT_ENCODING,'replace')
    6.     super(HTML, self).__init__(
    7.         # Convert unicode HTML to bytes.
    8.         element=PyQuery(html)('html') or PyQuery(f'<html>{html}</html>')('html'),
    9.         html=html,
    10.         url=url,
    11.         default_encoding=default_encoding
    12.     )
    复制代码
    代码 if isinstance(html, str): 用于判断 html 是否为 str,但是在实测过程中发现 html 是  类型,以是数据没有举行转码工作,故取消相干判断。
    除此以外,通过输出 world.html.encoding 发现网页的编码不是 GB2312 ,而是 gb18030,以是通过下述代码举行了默认编码的设置。
    1. requests_html.DEFAULT_ENCODING = "gb18030"
    复制代码
    按照如上内容举行修改之后,代码可以正常运行,数据能精确的采集到。
    本案例还新增了代码运行时长的计算,具体如下:
    1. # 获取开始时间
    2. start = time.perf_counter()
    3. # 执行代码的部分
    4. # 获取时间间隔
    5. elapsed = (time.perf_counter() - start)
    6. print("程序运行完毕,总耗时为:", elapsed)
    复制代码
    完整的代码运行效果如下所示:


    免责声明:部分资源来源于如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
    Lmmso-Loser 论坛免责声明
    论坛总版规:
    https://bbs.lmmso.loserc.com/thread-2-1-1.html
    免责声明:
    Lmmso-Loser 论坛发布的一切破解补丁、注册机和注册信息及软件的解密分析文章和网络工具及网络安全分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
    该会员没有填写今日想说内容.
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    Loading...

    QQ| 举报中心| 小黑屋| Lmmso-Loser 官方论坛 ( 渝ICP备:2023000236号-1 ) 渝公网安备 XXXXXXXXXXXXXXX号

    GMT+8, 2023-2-9 20:06 , Processed in 0.142970 second(s), 30 queries .

    Powered by Lmmso-Loser Discuz! X3.5 Licensed

    Copyright © 2023-2025, Tencent Cloud.

    快速回复 返回顶部 返回列表