Selenium及Headless Chrome抓取動態HTML頁面
一般的的靜態HTML頁面可以使用requests等庫直接抓取,但還有一部分比較複雜的動態頁面,這些頁面的DOM是動態生成的,有些還需要用戶與其點擊互動,這些頁面只能使用真實的瀏覽器引擎動態解析,Selenium和Chrome Headless可以很好的達到這種目的。
Headless Chrome
Headless Chrome 是 Chrome 瀏覽器的無界面形態,可以在不打開瀏覽器的前提下,使用所有Chrome支持的特性,在命令行中運行你的腳本。以前在爬蟲要使用Phantomjs來實現這些功能,但Phantomjs已經暫停開發,現在可以使用Headless Chrome來代替。
使用很簡單,保證chrome命令指向chrome瀏覽器的安裝路徑,ubuntu下為google-chrome。
輸出html:
1
google-chrome --headless --dump-dom https://www.cnblogs.com/
將目標頁面截圖:
1
2
3
google-chrome --headless --disable-gpu --screenshot https://www.cnblogs.com/
# 規定大小
google-chrome --headless --disable-gpu --screenshot --window-size=640,960 https://www.cnblogs.com/
保存為pdf:
1
google-chrome --headless --disable-gpu --print-to-pdf https://www.cnblogs.com/
以上文件會保存於當前目錄。
還可以使用--remote-debugging-port參數進行遠程調試:
1
google-chrome --headless --disable-gpu --no-sandbox --remote-debugging-port=9222 --user-data-dir="/d/cnblogs" http://www.cnblogs.com
--user-data-dir參數可以設定保存目錄,--user-agent參數可以設定請求agent。上述的命令打開了一個websocket調試介面對當前Tab內頁面的DOM、網路、性能、存儲等等進行調試。
打開http://127.0.0.1:9222/鏈接可以看到可檢查的網頁,可以點擊它們並看到使用了哪種Headless渲染。
還有一系列地址:
http://127.0.0.1:9222/json 查看已經打開的Tab列表:
1
2
3
4
5
6
7
8
9
[ {
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/5C7774203404DC082182AF4563CC7256",
"id": "5C7774203404DC082182AF4563CC7256",
"title": "博客園 - 代碼改變世界",
"type": "page",
"url": "https://www.cnblogs.com/",
"webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/5C7774203404DC082182AF4563CC7256"
} ]
http://127.0.0.1:9222/json/version : 查看瀏覽器版本信息
1
2
3
4
5
6
7
8
{
"Browser": "HeadlessChrome/71.0.3578.98",
"Protocol-Version": "1.3",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/71.0.3578.98 Safari/537.36",
"V8-Version": "7.1.302.31",
"WebKit-Version": "537.36 (@15234034d19b85dcd9a03b164ae89d04145d8368)",
"webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/ed156c0d-805c-4849-99d0-02e454260c17"
}
http://127.0.0.1:9222/json/new?http://www.baidu.com : 新開Tab打開指定地址
http://127.0.0.1:9222/json/close/8795FFF09B01BD41B1F2931110475A67 : 關閉指定Tab,close後為tab頁面的id
http://127.0.0.1:9222/json/activate/5C7774203404DC082182AF4563CC7256 : 切換到目標Tab
tab頁面信息中有一個devtoolsFrontendUrl,是開發者工具的前端地址,可以打開:
1
http://127.0.0.1:9222/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/CE2E627C634EAAE3CE9193DC374C7B4A
在開發者工具里切換到Performance,勾選Screenshots,點刷新圖標,重新載入完成就可以看到逐幀載入的截圖:
Selenium
Selenium 是用於測試 Web 應用程序用戶界面的常用框架,它支持各種瀏覽器,包括 Chrome,Safari,Firefox 等,支持多種語言開發,比如 Java,C,Ruby等等,當然也有Python。
1
pip install selenium
使用時還需要下載瀏覽器驅動,以chromedriver為例,下載地址:
chromedriver
國內鏡像:
鏡像
下載時注意與電腦的chrome版本保持一致,然後將chromedriver置於環境變數之中。
打開一個淘寶商品網頁:
1
2
3
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=568217064643")
瀏覽器會自動打開並訪問網頁。
使用headless模式:
1
2
3
4
5
6
7
8
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-gpu")
browser = webdriver.Chrome(options=chrome_options)
browser.get("https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=568217064643")
data = browser.page_source
page_souce屬性可以獲取html網頁源碼。
可以看到獲取的源碼都是些js與css語句,dom並未生成,需要模擬瀏覽器滾動來生成dom:
1
2
3
4
5
for i in range(1, 11):
browser.execute_script(
"window.scrollTo(0, document.body.scrollHeight/10*%s);" % i
)
time.sleep(0.5)
execute_script方法可以用來執行js腳本。
現在獲取的源碼基本是完整的,還存在一些小問題,比如網頁為了讓img延遲載入,img的地址是放在data-img屬性上的,等到瀏覽器滑動至圖片時才修改src屬性,可以使用pyquery修改:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import time
from selenium import webdriver
from pyquery import PyQuery as pq
base_dir = os.path.dirname(__file__)
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-gpu")
browser = webdriver.Chrome(options=chrome_options)
# browser.implicitly_wait(10)
browser.get("https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=568217064643")
for i in range(1, 11):
browser.execute_script(
"window.scrollTo(0, document.body.scrollHeight/10*%s);" % i
)
time.sleep(0.5)
data = browser.page_source.encode("utf-8")
doc = pq(data)
for img in doc("img"):
img = pq(img)
if img.attr["data-img"]:
img.attr.src = img.attr["data-img"]
data = doc.html(method="html").replace("src="//", "src="http://")
f = open(os.path.join(base_dir, "detail.html"), "w")
f.write(data.encode("utf-8"))
f.close()
保存為html後打開可以看到網頁爬取成功。
selenium還提供了很多element提取介面:
提取單個element:
1
elem = browser.find_element_by_id("description")
提取多個:
1
elem = browser.find_elements_by_class_name("detail-desc")
批量爬取
可以使用concurrent.futures 線程池進行多線程批量爬取:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# -*- coding: utf-8 -*-
import threading
import time
import os
from concurrent.futures import ThreadPoolExecutor, as_completed
from pyquery import PyQuery as pq
class TaobaoCrawler(object):
def __init__(self, ids):
self.ids = ids
self.browsers = {}
self.timeout_spus = []
self.url = "https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id="
def _create_new_browser(self):
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-gpu")
# chrome_options.add_argument("--blink-settings=imagesEnabled=false")
browser = webdriver.Chrome(options=chrome_options)
return browser
def get_browser(self):
current_thread_id = threading.currentThread().ident
existed = self.browsers.get(current_thread_id)
if existed:
return existed
new_browser = self._create_new_browser()
self.browsers[current_thread_id] = new_browser
return new_browser
def close_browsers(self):
for _, browser in self.browsers.iteritems():
browser.quit()
self.browsers = {}
def scroll_browser(self, browser, num):
"""模擬瀏覽器滾動 保證js全部執行完成"""
for i in range(1, num+1):
browser.execute_script(
"window.scrollTo(0, document.body.scrollHeight/%d*%d);" % (
num, i)
)
time.sleep(0.5)
def handle_detail_doc(self, detail):
doc = pq(detail)
for img in doc("img"):
img = pq(img)
if img.attr["data-img"]:
img.attr.src = img.attr["data-img"]
detail = doc.html(method="html")
detail = detail.replace("src="//", "src="http://")
return detail
def crawl_taobao_detail(self, taobao_id):
browser = self.get_browser()
url = self.url + str(taobao_id)
browser.execute_script("window.stop();")
browser.get(url)
self.scroll_browser(browser, 20)
data = browser.page_source.encode("utf-8")
data = self.handle_detail_doc(data)
return taobao_id, data
def start_crawl(self):
if not self.ids:
return
with ThreadPoolExecutor(max_workers=4) as executor:
futures = [executor.submit(self.crawl_taobao_detail, _)
for _ in self.ids]
for task in as_completed(futures):
if task.done():
taobao_id, data = task.result()
base_dir = os.path.dirname(__file__)
f = open(os.path.join(base_dir, str(taobao_id) + ".html"), "w")
f.write(data.encode("utf-8"))
f.close()
self.close_browsers()
def test_crawl():
ids = [568217064643, 584126060993, 581555053584, 581002124614]
c = TaobaoCrawler(ids)
c.start_crawl()
if __name__ == "__main__":
test_crawl()
作者:再見紫羅蘭
原文:https://www.cnblogs.com/linxiyue/p/10215912.html


TAG:程序員小新人學習 |