基于selenium 4模拟使用 "百度" 搜索内容,然后针对搜索结果实现网页数据抓取。本项目仅用于个人测试和学习。
域名 | 网址示例 | 支持 |
---|---|---|
百度 | "https://www.baidu.com" | ✅ |
百度百科 | "https://baike.baidu.com/item/花卉/229536" | ✅ |
百家号 | "https://baijiahao.baidu.com/s?id=1796368810044659826&wfr=spider&for=pc" | ✅ |
CSDN | "https://blog.csdn.net/weixin_43958105/article/details/114012590" | ✅ |
澎湃新闻 | "https://www.thepaper.cn/newsDetail_forward_27031399" | ✅ |
github每日热门项目 | "https://github.com/trending/python?since=daily" | ✅ |
selenium官方界面 | "https://www.selenium.dev/documentation/" | ✅ |
-
安装selenium。
-
安装Chrome driver。
安装Chrome driver教程可参考这里。
-
运行下列指令,安装依赖项。
pip install -r requirements.txt
- 更改
main.py
中的查找项。
if __name__ == "__main__":
start_time = time.time()
baidu_search = BaiduSearchScraper()
# 更改为你要查询的内容
rtn = baidu_search.fetch_webpage_content("澳亿万富豪之女在悉尼袭击案中遇害")
logger.info(f"百度搜索的结果为:{rtn}")
end_time = time.time()
execution_time = end_time - start_time
logger.info(f"执行时间为:{execution_time} 秒")
- 运行
main.py
文件。
python main.py
运行 main.py
文件后,会在项目根目录下自动生成 baidu_search_result.log
文件,爬取的内容将会写入其中。每次运行 main.py
文件,新的log信息会追加写入。
所有的测试文件均写在 examples
文件夹下,大家可以根据需要自行选择需要执行的文件。
-
网址尽量传入完整的网址(带有协议部分),如果传入的网址不含有协议,将自动在网址前添加 "https://"。
-
URL格式编码的网址可以直接传入,不需要解码。
例如 "https://baike.baidu.com/item/%E8%8A%B1%E5%8D%89/229536" 可以直接传入执行爬取操作,不需要解码为 "https://baike.baidu.com/item/花卉/229536"。
- 为提高爬取效率,默认采用低资源(
eager
)方式加载界面。用户如果想使用完全加载模式,可以参考以下写法重写__init__
方法。
class BaiduSearchScraper(WebSeleniumBase):
def __init__(self):
"""重写基类的init,启动浏览器
"""
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--no-sandbox')
options.page_load_strategy = 'eager' # 默认是normal模式
try:
self.driver = webdriver.Chrome(options=options)
logger.info("chrome浏览器启动成功!!!!")
except Exception as e:
logger.error(f"启动chrome时发生错误:{str(e)}")
def fetch_webpage_content(self, url):
# 省略
pass
-
selenium对于浏览器的关闭和开启很敏感,使用时一定要注意,避免出错。
-
抓取访问受限的网站时,使用selenium需要开启代理吗?
Selenium 是一个用于 自动化网页浏览器操作 的工具,主要用于测试网页应用。模拟的是打开浏览器操作的方式,如果你的浏览器默认是走代理的,代码中不需要再另外设置代理。
- 抓取某些外网数据很慢,如果你其他各种渠道已经尝试过了,可以考虑更换代理节点试试。
笔者的经验是: 同样是外网,抓取selenium官网的数据很快,但抓取github的数据很慢。
- 爬取的耗时如何?
项目使用的selenium,selenium为同步操作,单个网页爬取的耗时一般在2s内,如果网页内容特别多,耗时会增加(例如百科类网址)。
- 采用你的代码后,爬取对应网址的耗时严重,要如何解决?
selenium的网页加载策略笔者已经采用低资源模式了,如果耗时依旧严重,大家可以查看selenium的官网或chrome的开发网站寻找合适的参数。或者直接换异步框架(如 playwright)。
- 不能抓取目标网站的图片、视频吗?
项目爬取目标为网站文本,常规状态下不抓取图片、视频。大家可以根据自己的业务场景,添加合适的代码。
- 我想要的内容没有爬取到。
如果你想要的内容没有爬取到,请检查代码中的CSS选择器或XPATH。笔者的爬取逻辑是覆盖常用内容,部分内容不在这个范围内,大家可以根据自己的业务场景修改代码。
- 为什么我的代码结构正确,但是依旧提示
ERROR 元素未找到: Message: stale element reference: stale element not found
?具体结构如下:
class BaiduSearchScraper(WebSeleniumBase):
def __init__(self):
pass
# 省略
def fetch_content(self):
fetched_content = []
try:
# 定位搜索结果
search_result = self.driver.find_elements(By.CSS_SELECTOR, '.tts-button_1V9FA')
for each_search_result in search_result:
hyperlink = each_search_result.get_attribute('data-url') #
logger.info(f"\n当前待爬取链接为:{hyperlink}\n")
# fetched_content.append(hyperlink)
crawl_content = self.crawl_text_content(hyperlink)
fetched_content.append(crawl_content)
except Exception as e:
logger.error(f"元素未找到: {str(e)}")
return fetched_content
def crawl_text_content(self, hyperlink):
"""根据网址爬取文本内容
"""
crawl_content = {}
self.driver.get(hyperlink)
self.driver.implicitly_wait(5)
# 百度搜索引擎的部分网址为"重定向链接"("redirected_link"),不是"实际链接"("direct_link")。
direct_link = self.driver.current_url
logger.info(f"实际链接为:{direct_link}")
return crawl_content
这是因为,你尝试获取某个网页元素的属性,然后使用这个属性(如一个链接)来访问另一个页面。在这个过程中,原来的网页环境发生了变化(例如,由于页面跳转到了新的URL),那么原始的网页元素会变得不再有效,导致这种错误。
即在 crawl_text_content
方法中,使用 self.driver.get(hyperlink)
访问了新的URL,这导致原来的 search_result
中的元素失效。当尝试在循环中处理下一个元素时,原来的元素已经不在DOM中了,因此报出了“stale element reference”的错误。
在处理动态页面和导航时,selenium的表现与普通的用户操作非常相似。当你使用 self.driver.get()
方法导航到一个新的页面时,WebDriver 的上下文会转移到新页面,原页面的 DOM 结构会被卸载。这就是为什么原来通过 find_elements
获取的元素集合会变得不再有效的原因。
🔥🔥🔥解决方案为修改 fetch_content
方法,使其先收集所有链接,然后再逐一处理::
def fetch_content(self):
fetched_content = []
try:
search_result = self.driver.find_elements(By.CSS_SELECTOR, '.tts-button_1V9FA')
hyperlinks = [result.get_attribute('data-url') for result in search_result]
for hyperlink in hyperlinks:
crawl_content = self.crawl_text_content(hyperlink)
fetched_content.append(crawl_content)
except Exception as e:
logger.error(f"元素未找到: {str(e)}")
return fetched_content
由于爬虫的特殊性,需要根据网页的变化而更改代码,大家在使用的过程中发现某个网址爬取失败,欢迎提交issue。
大家如果有想要爬取的网页,也欢迎在issue中提出,笔者优先撰写相关代码。
Selenium 是同步爬取,耗时问题难以解决。笔者决定转战异步爬取工具 playwright。