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为例,下载地址:
国内镜像:
下载时注意与电脑的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() |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
2018-01-04 Redis锁构造