
文章目錄
- 前言
- 執行緒鎖
- retrying模塊
- aiohttp模塊
- requests-html模塊
- 獲取網頁原始碼
- 獲取鏈接
- 獲取元素
- JavaScript支持!!!
- 騷操作:合并ts視頻
前言
這兩天都沒更新啥哈,太忙了,
今天這篇會比較特殊一些,因為爬蟲我會的也寫的差不多了,但是我還有一個背后隱藏能源還沒拿出來用呢!!!
今天就打開這個隱藏能源,這可是一個大佬多年嘔心瀝血打造的,看看我能從中學到多少東西,
執行緒鎖
哈哈,先來就講這個和爬蟲“不太沾邊”的東西,是不是不好啊,
其實是很有關系的啦,如果你要靠自己大批量爬取內容,肯定是離不開多執行緒作業了嘛,要是單執行緒那速度你會很感動的,
但是多執行緒就有一個通病,資源競態,這是跑不掉的,
那怎么解決這個問題,最直接的一個辦法就是上鎖,
all_urls = [] #url陣列
g_lock = threading.Lock() #初始化一個鎖
while len(all_urls) > 0 :
g_lock.acquire() #在訪問all_urls的時候,需要使用鎖機制
page_url = all_urls.pop() #通過pop方法移除最后一個元素,并且回傳該值
g_lock.release() #使用完成之后及時把鎖給釋放,方便其他執行緒使用
······
retrying模塊
當我們用 request 發起網路請求,時不時會遇到超時,當然不可能讓這個請求一直阻塞,一般會設定一個超時時間,用 try except 拋出例外,避免程式中斷,我們日常訪問某網站時,有打不開的情況都會多重繪幾次,因此,我們也需要讓 python 進行重試,而 retrying 模塊應運而生
為了表現 retrying 的重試功能,我們故意請求一個不規范的鏈接,如 www.baidu.com ,由于沒有帶 http 協議,request 會報錯,從而觸發 retrying 重試:
import requests
from retrying import retry
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}
@retry(stop_max_attempt_number=3) # 表示重試以下代碼三次
def _parse_url(url):
print("-" * 30)
response = requests.get(url, headers=headers, timeout=3)
assert response.status_code == 200
return response.content.decode()
def parse_url(url):
try:
html_str = _parse_url(url)
except:
html_str = None
return html_str
if __name__ == "__main__":
url = 'www.baidu.com'
print(parse_url(url))
接著,你再把http加上去試試看,
aiohttp模塊
在 Python 眾多的 HTTP 客戶端中,最有名的莫過于requests、aiohttp和httpx(說實話,今天之前,我只知道requests和那個urllib),
在不借助其他第三方庫的情況下,requests只能發送同步請求;aiohttp只能發送異步請求;httpx既能發送同步請求,又能發送異步請求,
同步和異步的概念:同步是串行,異步是并發,同步請求就是請求完了一個再請求一個,異步請求就是不管有沒有結果回來,繼續往下請求再說,
實在不知道要怎么說,來段代碼吧:
import aiohttp
import asyncio
async def fetch_img_url(num):
url = f'http://bbs.fengniao.com/forum/forum_101_{num}_lastpost.html' # 字串拼接
# 或者直接寫成 url = 'http://bbs.fengniao.com/forum/forum_101_1_lastpost.html'
print(url)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6726.400 QQBrowser/10.2.2265.400',
}
async with aiohttp.ClientSession() as session: # 創建一個會話,用于保持連接,保持連接的好處我們心照不宣哈
# 獲取輪播圖地址
async with session.get(url, headers=headers) as response:
try:
html = await response.text() # 獲取到網頁原始碼 # await:等待網頁資料回傳
print(html)
except Exception as e:
print("基本錯誤")
print(e)
loop = asyncio.get_event_loop() #創建執行緒
tasks = asyncio.ensure_future(fetch_img_url(1))
results = loop.run_until_complete(tasks) #負責安排tasks中的任務
# task可以是單獨的函式,也可以是串列
httpx我希望各位可以回去自行查找資料,我不急,我開班了再說這個,
這里帶上一個大佬的評論:
tasks并不是只能開1024個協程,只是最多只能同時運行1024個協程(實際上會低于這個數),因為asycnio內部使用的是select ,而select的其中一個局限性就是最多只能有1024個fd(檔案描述法),官方不使用poll和epoll,我猜測是為了Windows的兼容性,因為后面兩個雖然強大但不支持Windows,還有開多少個協程都只是充分利用CPU,你放心,它只是持續處于亢奮狀態,應該不會吃不消,
受教了,
requests-html模塊
requests-html和其他決議HTML庫最大的不同點在于HTML決議庫一般都是專用的,所以我們需要用另一個HTTP庫先把網頁下載下來,然后傳給那些HTML決議庫,而requests-html自帶了這個功能,所以在爬取網頁等方面非常方便,
不多廢話,直接看它能干嘛,怎么干!!!
獲取網頁原始碼
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('url')
// 查看頁面內容
print(r.html.html)
獲取鏈接
# 獲取鏈接
print(r.html.links) # 獲取所有鏈接
print(r.html.absolute_links) # 獲取全部絕對鏈接

獲取元素
這里只講Xpath,這需要另一個函式xpath的支持,它有4個引數如下:
- selector,要用的XPATH選擇器;
- clean,布林值,如果為真會忽略HTML中style和script標簽造成的影響(原文是sanitize,大概這么理解);
- first,布林值,如果為真會回傳第一個元素,否則會回傳滿足條件的元素串列;
- _encoding,編碼格式,
print(r.html.xpath("//div[@id='menu']", first=True).text)
print(r.html.xpath("//div[@id='menu']/a"))
print(r.html.xpath("//div[@class='content']/span/text()"))
懂得都懂,不再贅述,不懂私聊,
JavaScript支持!!!
這個牛逼了,好多次被JavaScript攔在了門口,
有些網站是使用JavaScript渲染的,這樣的網站爬取到的結果只有一堆JS代碼,這樣的網站requests-html也可以處理,關鍵一步就是在HTML結果上呼叫一下render函式,它會在用戶目錄(默認是~/.pyppeteer/)中下載一個chromium,然后用它來執行JS代碼,下載程序只在第一次執行,以后就可以直接使用chromium來執行了,唯一缺點就是chromium下載實在太慢了,
>>> r = session.get('http://python-requests.org/')
>>> r.html.render()
[W:pyppeteer.chromium_downloader] start chromium download.
Download may take a few minutes.
[W:pyppeteer.chromium_downloader] chromium download done.
[W:pyppeteer.chromium_downloader] chromium extracted to: C:\Users\xxxx\.pyppeteer\local-chromium\571375
>>> r.html.search('Python 2 will retire in only {months} months!')['months']
'<time>25</time>'
render函式還有一些引數,順便介紹一下(這些引數有的還有默認值,直接看源代碼方法引數串列即可):
- retries: 加載頁面失敗的次數
- script: 頁面上需要執行的JS腳本(可選)
- wait: 加載頁面錢的等待時間(秒),防止超時(可選)
- scrolldown: 頁面向下滾動的次數
- sleep: 在頁面初次渲染之后的等待時間
- reload: 如果為假,那么頁面不會從瀏覽器中加載,而是從記憶體中加載
- keep_page: 如果為真,允許你用r.html.page訪問頁面
比如說簡書的用戶頁面上用戶的文章串列就是一個異步加載的例子,初始只顯示最近幾篇文章,如果想爬取所有文章,就需要使用scrolldown配合sleep引數模擬下滑頁面,促使JS代碼加載所有文章,
下次再遇到這種情況就有據可循了,
騷操作:合并ts視頻
M3U8檔案是指UTF-8編碼格式的M3U檔案,紀錄了一個索引純文本檔案,打開視頻播放的時候并不是播放它,而是根據索引找到音視頻所在的網址進行播放操作,
原視頻資料分割為很多個TS流,每個TS流的地址記錄在m3u8檔案串列中
講M3U8可能各位不熟悉,那這個TS流呢?如果沒有被TS流折磨過,可能是比較老實一點吧,
那ts 檔案一般怎么處理?一般有這么幾種情況:···
我們今天就講這么一種情況:ts檔案能正常播放,但太多而小,需要合并,
import os
from os import path
def file_walker(path):
file_list = []
for root, dirs, files in os.walk(path): # 生成器
for fn in files:
p = str(root+'/'+fn)
file_list.append(p)
print(file_list)
return file_list
def combine(ts_path, combine_path, file_name):
file_list = file_walker(ts_path)
file_path = combine_path + file_name + '.ts'
with open(file_path, 'wb+') as fw:
for i in range(len(file_list)):
fw.write(open(file_list[i], 'rb').read())
if __name__ == '__main__':
#urls = get_ts_urls("playlist.m3u8","https://videos5.jsyunbf.com/2019/02/07/iQX7y3p1dleAhIv7/")
#download(urls,"./tsfiles")
combine("./ts_files","d:/ts","haha")
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/271360.html
標籤:python
