本篇博客是《爬蟲 120 例》的第 30 例,新學習一個爬蟲框架 requests-html,該框架作者就是 requests 的作者,所以盲猜就很好用啦,
知識鋪墊作業
requests-html 模塊安裝使用 pip install requests-html 即可,官方手冊查詢地址:https://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 個案例進行學習掌握,接下來進入第一個案例,
目標站點分析
本次要采集的目標網站為:http://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.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
其余內容相對比較簡單,頁碼規則如下:
http://www.world68.com/top.asp?t=5star&page=1
http://www.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://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>dt>a')通過 css 選擇器,查找所有的網頁標題元素;item.text提取網頁標題內容;item.attrs['href']獲取元素屬性,即網站域名,
運行效果如下所示,獲取到的 3519 個站點,就不在提供了,簡單運行 1 分鐘代碼,即可得到,

由于上述代碼太少了,完全不夠今日代碼量,我們順手將其修改為多執行緒形式,
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://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 是 <class 'bytes'> 型別,所以資料沒有進行轉碼作業,故取消相關判斷,
除此以外,通過輸出 world.html.encoding 發現網頁的編碼不是 GB2312 ,而是 gb18030,所以通過下述代碼進行了默認編碼的設定,
requests_html.DEFAULT_ENCODING = "gb18030"
按照如上內容進行修改之后,代碼可以正常運行,資料能正確的采集到,
本案例還新增了代碼運行時長的計算,具體如下:
# 獲取開始時間
start = time.perf_counter()
# 執行代碼的部分
# 獲取時間間隔
elapsed = (time.perf_counter() - start)
print("程式運行完畢,總耗時為:", elapsed)
完整的代碼運行效果如下所示:

收藏時間
代碼倉庫地址:https://codechina.csdn.net/hihell/python120,去給個關注或者 Star 吧,
資料沒有采集完畢,想要的可以在評論區留言交流
今天是持續寫作的第 212 / 365 天,
可以關注我,點贊我、評論我、收藏我啦,
更多精彩
- Python 爬蟲 100 例教程導航帖(已完結,復盤更新中,目前 110+ 篇)

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/298122.html
標籤:python
