关于如何实现爬虫,可以简单看这里:入门级别的难度,两个例子 。
更主要的目的是,把当时上课讲过的Selenium
整理一下,毕竟当时我还做PPT
上去讲课来,Selenium
虽然是自动化测试工具,但也可以很好的用在爬虫领域。
在很多网页中使用了Ajax
技术,即:你往下翻阅或者点击加载更多按钮才会加载剩下的内容,否则不会加载你暂时不看的内容。或者网页使用了反爬虫技术,爬取速度过快会严重影响对方服务器的流量,提高对方的成本,因此对方服务器会把你拉黑防止你的爬虫行为。
如何解决呢?
对于第一类问题,可以靠Selenium
模拟网页浏览、按钮点击动作解决; 对于第二类问题,可以设置两种等待方式,放缓爬取速度,避免被拉黑。
所需软件和库:
selenium
pip install 即可
chromeWebdriver
下载安装 (firefox也有这个类似的)。我放到了D盘的根目录下,所以路径为’D:/chromedriver.exe’。
模拟按钮点击下一页 以著名的ICU996
为例,我想看哪些人star
了这个项目,且star
这也页面的域名毫无规律可言,必须使用点击下一页的按钮才能看到下一页的结果。
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 import requestsfrom bs4 import BeautifulSoupimport pandas as pdimport numpy as npimport timefrom selenium import webdriverstar_url = r"https://github.com/996icu/996.ICU/stargazers" driver = webdriver.Chrome(r'D:/chromedriver.exe' ) driver.get(star_url) name_counter = 1 page = 0 ; while page < 500 : print (page) soup = BeautifulSoup(driver.page_source, "lxml" ) data = p.findall(str (soup.body)) for i in data: i = i.strip('Works for "' ) if i in df.index: po=df.loc[[i],:] po.Sum += 1 else : df.loc[i] = 1 driver.find_element_by_xpath("//a[contains(text(),'Next')]" ).click() page = page + 1 time.sleep(0.5 ) df.to_csv('9961.csv' , sheet_name = 'watch' )
这样一个简单的代码就完成了,但是爬下来的数据 没有经过处理,如NCST
,NCUST
和华北理工大学表示的是一个东西,数据应该经过后期处理整合,但这里在说爬虫,到此为止。
在上面的例子中,已经介绍了一种等待方式:显示等待,time.sleep(0.5)
,而这也是显示等待中最糟糕的一个。即到这里强行停止0.5秒,然后在访问下一页,防止爬中速度快被拉黑。
模拟浏览器翻阅 主要是用代码控制浏览器,完成向下翻阅浏览的功能,两点好处:
使服务器辨别不出来是人在看他的网页还是机器在看他的网页。
触发Ajax
,使浏览器自动加载剩下的内容,而不用使人去手动翻阅加载。如下图所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 from selenium import webdriverdriver = webdriver.Chrome(r'D:/chromedriver.exe' ) def scroll_down (num ): for i in range (num): driver.execute_script("window.scrollBy(0,1000)" ) time.sleep(2 ) scroll_down(10 )
(0, 1000)
中的0
表示水平翻阅0个像素点,1000
表示向下翻阅1000个像素点。还记得屏幕的分辨率是1920X1080
吗,1080就是指竖直方向排列的像素点的数量。记住这段代码,一会儿配合其他任务一起用。
设置页面等待 隐性等待 官方文档永远是第一:https://selenium-python-zh.readthedocs.io/en/latest/waits.html
如果某些元素不是立即可用的(可能页面不会第一时间加载该元素,可能浏览器翻阅到一定位置才加载),隐式等待是告诉WebDriver去等待一定的时间后去查找元素。 默认等待时间是0秒,一旦设置该值,隐式等待是设置该WebDriver的实例的生命周期(意思是:设置隐性等待30秒的话,全部代码全部的等待时间是30秒,第一次等了12秒,后续代码就只有剩下的18秒可用了)。
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 from selenium import webdriverimport timedriver = webdriver.Chrome(r'D:/chromedriver.exe' ) driver.implicitly_wait(10 ) driver.get('https://cn.bing.com' ) try : driver.find_element_by_id('sb_form_q' ) print ('yes' ) except : print ('false' ) since = time.time() try : driver.find_element_by_id('bs_form_q' ) print ('yes 1' ) except : print ('false 1' ) end = time.time() finally : driver.quit() print (end - since)
输出如下:
1 2 3 yes false 1 10.114457607269287
因为第一次能找到,所以输出yes
,第二次找不到,等了10秒,输出了false
。重点:一定要用异常捕获,否则寻找不到代码会强行停止,影响后续代码执行。
当然隐性等待只用于加载元素,而不是
打开网页:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from selenium import webdriverimport timedriver = webdriver.Chrome(r'D:/chromedriver.exe' ) driver.implicitly_wait(10 ) page = [ "https://www.qq.com/" , "https://baidu.com" , "https://cn.bing.com" , "https://www.google.com/" , "https://baidu.com" ] for url in page: since = time.time() driver.get(url) end = time.time() print (end - since) driver.quit()
输出如下(第四行输出显示:等了三十秒没打开网页 已经超出了设置的10秒,所以隐形等待是用来加载元素而不是加载整个网页):
1 2 3 4 5 3.0922935009002686 1.215383768081665 13.705082416534424 33.13365077972412 0.6389157772064209
实例 以ECharts数据可视化实验室网站 为例,(百度 ECharts 团队创建,联合公司内外众多数据可视化从业人组成的技术研究虚拟组织,致力于数据可视化的相关研究、教育普及、产品研发及生态建设。),虽然是百度的,但是这个可视化网站还不错。
而这个网站使用了Ajax
技术,不往下翻阅是不会加载内容的,正好用上之前写的控制浏览器翻阅的代码。
我往下翻阅了很久,有个实例的作者叫 隐居威海 ,我们来爬取这个作者。(当然那个网站一直在变动,可能这个名字没了,或者加入了新的用例需要多翻阅几次,或者本次代码实效都很正常,主要介绍理念。我几个月前还是可以运行的。)
以下代码的效果:不往下翻阅浏览器加载不出来,只有向下加载才能定位到目标元素。意思是,不写scroll_down(10)
会报错,写了这个函数才能爬取到。
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 from selenium import webdriverimport timedriver = webdriver.Chrome(r'D:/chromedriver.exe' ) driver.implicitly_wait(30 ) driver.get("https://gallery.echartsjs.com/explore.html#sort=rank~timeframe=all~author=all" ) def scroll_down (num ): for i in range (num): driver.execute_script("window.scrollBy(0,1000)" ) time.sleep(2 ) try : since = time.time() scroll_down(10 ) print (driver.find_element_by_link_text('隐居威海' ).text) end = time.time() except : end = time.time() print ('loading....' ) finally : print (end - since) driver.quit()
写scroll_down(10)
的输出:
不写scroll_down(10)
的输出
1 2 loading.... 30.12252512365
显性等待 第一种等待方式time.sleep
已经在模拟按钮点击下一页
这一内容下介绍,并给出了程序实例,这也是最糟糕的。强制让浏览器等待X秒,不管当前操作是否完成,是否可以进行下一步操作,都必须等X秒的时间。
另一种显示等待:程序每隔xx秒看一眼,如果条件成立了,则执行下一步,否则继续等待,直到超过设置的最长时间,然后抛出TimeoutException。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from selenium import webdriverfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byimport timedriver = webdriver.Chrome(r'D:/chromedriver.exe' ) driver.implicitly_wait(15 ) driver.get('https://liam.page/' ) locator = (By.LINK_TEXT, 'NexT.Gemini' ) since = time.time() try : WebDriverWait(driver, 10 , 0.5 ).until(EC.presence_of_element_located(locator)) print ('yes' ) except : print ('no' ) end = time.time() finally : end = time.time() driver.close() print (end - since)
输出:
1 2 yes 0.019946813583374023
可见用了不到一秒的时间加载了该元素。
结语 如果我们设置了隐性等待和显性等待,最长的等待时间取决于两者之间的大者,如果隐性等待时间 > 显性等待时间,则该句代码的最长等待时间等于隐性等待时间。
可以很好的利用自动化测试工具Selenium
的元素查找、等待模式来完成爬虫中的工作。
等哪天心血来潮再去看看多线程异步爬虫吧。