Flask利用自定义接口实现mock应用详解
452
2022-09-03
【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.(额,这最后一点,各位自己领悟吧)
Only Python 3.6 is supported. 仅支持 Python 3.6 ,实测发现 3.6 以上版本依旧可以。
对于该库的简单使用,代码如下所示:
from requests_html import HTMLSession session = HTMLSession() r = session.get('https://python.org/') print(r)
首先从 requests_html 库导入 HTMLSession 类,然后将其实例化之后,调用其 get 方法,发送请求,得到的 r 输出为 <Response [200]>,后续即可使用内置的解析库对数据进行解析。
由于该库是解析 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:同上,返回的全部数据;
html 对象的属性包括
links:返回页面所有链接; absolute_links:返回页面所有链接的绝对地址; base_url:页面的基准 URL; html,raw_html,text:以 HTML 格式输入页面,输出未解析过的网页,提取页面所有文本;
有了上述内容铺垫之后,在进行 Python 爬虫的编写就会变的容易许多,requests-html 库将通过 3~4 个案例进行学习掌握,接下来进入第一个案例。
怎么用
目标站点分析
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' def 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
其余内容相对比较简单,页码规则如下:
world68.com/top.asp?t=5star&page=1 world68.com/top.asp?t=5star&page=2
累计页数直接在底部进行了展示,可以设计为用户手动输入,即 input 函数实现。
目标数据存储网站名与网站地址即可,基于此,开始编码。
编码时间
首先通过单线程实现 requests-html 的基本逻辑,注意到下述代码非常轻量,
from requests_html import HTMLSession session = HTMLSession() page_size = int(input("请输入总页码:")) for page in range(1, page_size + 1): world = session.get(f'http://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>dt>a') 通过 css 选择器,查找所有的网页标题元素; item.text 提取网页标题内容; item.attrs['href'] 获取元素属性,即网站域名。
运行效果如下所示,获取到的 3519 个站点,就不在提供了,简单运行 1 分钟代码,即可得到。![requests-html库初识 + 无资料解BUG之 I/O error : encoder error,Python爬虫第30例](=400x)由于上述代码太少了,完全不够今日代码量,我们顺手将其修改为多线程形式。
import requests_html import threading import time import fcntl class 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 = f'http://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, url=url, default_encoding=default_encoding )
代码 if isinstance(html, str): 用于判断 html 是否为 str,但是在实测过程中发现 html 是 <class 'bytes'> 类型,所以数据没有进行转码工作,故取消相关判断。
除此以外,通过输出 world.html.encoding 发现网页的编码不是 GB2312 ,而是 gb18030,所以通过下述代码进行了默认编码的设置。
requests_html.DEFAULT_ENCODING = "gb18030"
按照如上内容进行修改之后,代码可以正常运行,数据能正确的采集到。
本案例还新增了代码运行时长的计算,具体如下:
# 获取开始时间 start = time.perf_counter() # 执行代码的部分 # 获取时间间隔 elapsed = (time.perf_counter() - start) print("程序运行完毕,总耗时为:", elapsed)
完整的代码运行效果如下所示:
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~