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:

google-chrome --headless --dump-dom https://www.cnblogs.com/

   将目标页面截图:

1google-chrome --headless --disable-gpu --screenshot https://www.cnblogs.com/ 2# 规定大小 3google-chrome --headless --disable-gpu --screenshot --window-size=640,960 https://www.cnblogs.com/

 保存为pdf:

google-chrome --headless --disable-gpu --print-to-pdf https://www.cnblogs.com/

 以上文件会保存于当前目录。

   还可以使用--remote-debugging-port参数进行远程调试:

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 "description": "", 3 "devtoolsFrontendUrl": "/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/5C7774203404DC082182AF4563CC7256", 4 "id": "5C7774203404DC082182AF4563CC7256", 5 "title": "博客园 - 代码改变世界", 6 "type": "page", 7 "url": "https://www.cnblogs.com/", 8 "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/5C7774203404DC082182AF4563CC7256" 9} ]

  http://127.0.0.1:9222/json/version : 查看浏览器版本信息

1{ 2 "Browser": "HeadlessChrome/71.0.3578.98", 3 "Protocol-Version": "1.3", 4 "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/71.0.3578.98 Safari/537.36", 5 "V8-Version": "7.1.302.31", 6 "WebKit-Version": "537.36 (@15234034d19b85dcd9a03b164ae89d04145d8368)", 7 "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/ed156c0d-805c-4849-99d0-02e454260c17" 8}

   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,是开发者工具的前端地址,可以打开:

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。

pip install selenium

使用时还需要下载浏览器驱动,以chromedriver为例,下载地址:

chromedriver

国内镜像:

镜像

下载时注意与电脑的chrome版本保持一致,然后将chromedriver置于环境变量之中。

打开一个淘宝商品网页:

1from selenium import webdriver 2browser = webdriver.Chrome() 3browser.get('https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=568217064643')

浏览器会自动打开并访问网页。

使用headless模式:

1from selenium import webdriver 2chrome_options = webdriver.ChromeOptions() 3chrome_options.add_argument('--no-sandbox') 4chrome_options.add_argument('--headless') 5chrome_options.add_argument('--disable-gpu') 6browser = webdriver.Chrome(options=chrome_options) 7browser.get('https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=568217064643') 8data = browser.page_source

  page_souce属性可以获取html网页源码。

可以看到获取的源码都是些js与css语句,dom并未生成,需要模拟浏览器滚动来生成dom:

1for i in range(1, 11): 2 browser.execute_script( 3 "window.scrollTo(0, document.body.scrollHeight/10*%s);" % i 4 ) 5 time.sleep(0.5)

execute_script方法可以用来执行js脚本。

现在获取的源码基本是完整的,还存在一些小问题,比如网页为了让img延迟加载,img的地址是放在data-img属性上的,等到浏览器滑动至图片时才修改src属性,可以使用pyquery修改:

1import time 2 3from selenium import webdriver 4from pyquery import PyQuery as pq 5 6 7base_dir = os.path.dirname(__file__) 8chrome_options = webdriver.ChromeOptions() 9chrome_options.add_argument('--no-sandbox') 10chrome_options.add_argument('--headless') 11chrome_options.add_argument('--disable-gpu') 12browser = webdriver.Chrome(options=chrome_options) 13# browser.implicitly_wait(10) 14browser.get('https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=568217064643') 15for i in range(1, 11): 16 browser.execute_script( 17 "window.scrollTo(0, document.body.scrollHeight/10*%s);" % i 18 ) 19 time.sleep(0.5) 20data = browser.page_source.encode('utf-8') 21doc = pq(data) 22for img in doc('img'): 23 img = pq(img) 24 if img.attr['data-img']: 25 img.attr.src = img.attr['data-img'] 26data = doc.html(method='html').replace('src="https://my.oschina.net//', 'src="http://') 27f = open(os.path.join(base_dir, 'detail.html'), 'w') 28f.write(data.encode('utf-8')) 29f.close()

 保存为html后打开可以看到网页爬取成功。

 selenium还提供了很多element提取接口:

提取单个element:

elem = browser.find_element_by_id("description")

提取多个:

elem = browser.find_elements_by_class_name("detail-desc")

批量爬取

可以使用concurrent.futures 线程池进行多线程批量爬取:

1# -*- coding: utf-8 -*- 2import threading 3import time 4import os 5 6from concurrent.futures import ThreadPoolExecutor, as_completed 7from pyquery import PyQuery as pq 8 9 10class TaobaoCrawler(object): 11 def __init__(self, ids): 12 self.ids = ids 13 self.browsers = {} 14 self.timeout_spus = [] 15 self.url = 'https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=' 16 17 def _create_new_browser(self): 18 from selenium import webdriver 19 chrome_options = webdriver.ChromeOptions() 20 chrome_options.add_argument('--no-sandbox') 21 chrome_options.add_argument('--headless') 22 chrome_options.add_argument('--disable-gpu') 23 # chrome_options.add_argument('--blink-settings=imagesEnabled=false') 24 browser = webdriver.Chrome(options=chrome_options) 25 return browser 26 27 def get_browser(self): 28 current_thread_id = threading.currentThread().ident 29 existed = self.browsers.get(current_thread_id) 30 if existed: 31 return existed 32 new_browser = self._create_new_browser() 33 self.browsers[current_thread_id] = new_browser 34 return new_browser 35 36 def close_browsers(self): 37 for _, browser in self.browsers.iteritems(): 38 browser.quit() 39 self.browsers = {} 40 41 def scroll_browser(self, browser, num): 42 '''模拟浏览器滚动 保证js全部执行完成''' 43 for i in range(1, num+1): 44 browser.execute_script( 45 "window.scrollTo(0, document.body.scrollHeight/%d*%d);" % ( 46 num, i) 47 ) 48 time.sleep(0.5) 49 50 def handle_detail_doc(self, detail): 51 doc = pq(detail) 52 for img in doc('img'): 53 img = pq(img) 54 if img.attr['data-img']: 55 img.attr.src = img.attr['data-img'] 56 detail = doc.html(method='html') 57 detail = detail.replace('src="https://my.oschina.net//', 'src="http://') 58 return detail 59 60 def crawl_taobao_detail(self, taobao_id): 61 browser = self.get_browser() 62 url = self.url + str(taobao_id) 63 browser.execute_script("window.stop();") 64 browser.get(url) 65 self.scroll_browser(browser, 20) 66 data = browser.page_source.encode('utf-8') 67 data = self.handle_detail_doc(data) 68 return taobao_id, data 69 70 def start_crawl(self): 71 if not self.ids: 72 return 73 with ThreadPoolExecutor(max_workers=4) as executor: 74 futures = [executor.submit(self.crawl_taobao_detail, _) 75 for _ in self.ids] 76 for task in as_completed(futures): 77 if task.done(): 78 taobao_id, data = task.result() 79 base_dir = os.path.dirname(__file__) 80 f = open(os.path.join(base_dir, str(taobao_id) + '.html'), 'w') 81 f.write(data.encode('utf-8')) 82 f.close() 83 self.close_browsers() 84 85 86def test_crawl(): 87 ids = [568217064643, 584126060993, 581555053584, 581002124614] 88 c = TaobaoCrawler(ids) 89 c.start_crawl() 90 91if __name__ == '__main__': 92 test_crawl()
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )