查看原文
其他

工具&方法丨经生小白会敲代码,还会写爬虫防坑指南

天天向尚 数据Seminar 2021-06-04

引言

大家好,我是练习时长半个月的网络爬虫练习生向尚(律师函警告⚠️)

(不好意思,走错片场了)

NG重来(指导老师说写文章跟做导演其实是差不多的)。
我是杭州某省属高校。。。(总之除了名字,和上一期Wallace完全一样背景),导师忽悠我说深知掌握大数据采集、清理与分析相关技术的重要性。因为,随着互联网的快速发展……信息技术……大数据……经济学研究……计量方法的运用越来越重要。( 实在想不出了此处省略大数据收集、清理与分析对经济学研究的重要性的5000字。)
之前与Wallace一样,通过参加小刘老师主持的python培训(点此回顾),知晓一些Python的数据类型、简单循环、方法和函数,以及一些爬虫的知识……对了!我还拿到了小刘老师签发的结业证书。

半个月前,由于深得技术部主管小刘老师的“信任”(哈哈哈!),交给了我一项十分“简单”的爬虫任务,开场白是这样子的——

四五天没问题吧?

没问题!

然鹅,事实是这样子的——

本以为就真的很简单好嘛......没想到这一做就是半个月!好吧,这个故事再次告诉我们,学过和能够实践根本不是一回事啊!(为暑假大批参与python爬虫培训的学员小白们"加油”三秒钟~)
好歹连滚带爬完成任务,这期间受过的苦,踩过的坑,报过的错,还要经历仿佛上学没交作业的那种惴惴不安,不(非)足(常)为(值)外(得)人(说)道(一)也(说)。为了可以给路过之人(特别是未来要"入坑”的相关专业学弟学妹)一些参考和建议,我搜集整理了在运用selenium这一第三方库爬虫时遇到的各种bug及解决方案,形成这篇《爬虫初级防坑指南》,希望能够对大家有所帮助。


准备工作

开始之前,我们简单了解一下selenium爬虫的原理 。为方便初学者及吃瓜群众更好地理解selenium爬虫过程,我整理了所作项目的流程图:

搭建环境:
1)使用pip安装selenium库,在CMD窗口输入:
pip install selenium
左右滑动查看更多
2)安装webdriver,统一使用谷歌浏览器:
下载链接  https://sites.google.com/a/chromium.org/chromedriver/downloads
3)import导入第三方库
from selenium import webdriver
左右滑动查看更多
这里以百度搜索为例,将会完成百度搜索“python”,并点击进入python官方网站的操作。
然后就可以开始编写代码啦!
engine_path = r'C:\Users\Administrator\Desktop\chromedriver_win32.exe' # 插件的地址options = webdriver.ChromeOptions() options.add_argument("--headless") # 不加这条试试这条会发生什么# 低配版请求参数就算搞定了,可以用这个driver虚拟浏览器访问啦,复杂的还可加上请求头设置,ip设置等driver = webdriver.Chrome(executable_path=engine_path, options=options)

driver.get('https://www.baidu.com/') # 访问网址 driver.find_element_by_id("kw").send_keys("python") # 给id为"kw"的元素即搜索框输入信息"python"driver.find_element_by_id("su").click() # 点击百度一下

左右滑动查看更多

除了get()之外,以下列举了一些对网页的操作:

driver.current_url:用于获得当前页面的URL。

driver.title:用于获取当前页面的标题。

driver.page_source:用于获取页面html源代码。

driver.forward():浏览器向前(点击向前按钮)。

driver.back():浏览器向后(点击向后按钮)。

driver.refresh():浏览器刷新(点击刷新按钮)。

driver.close():关闭当前窗口,或最后打开的窗口。

driver.quit():关闭所有窗口。

driver.switch_to_window(窗口句柄):切换到新窗口。

打开网页后,我们需要找到网页内的目标元素。一般找元素的方法为在谷歌浏览器按F12打开开发者工具,用开发者工具左上角的小鼠标找到你需要的元素并复制xpath语句到程序中(具体方法可以参考Wallace同学的文章),以下列举了一些定位元素的方法:

find_element_by_id() # id定位find_element_by_name() # name定位find_element_by_class_name() # class定位find_element_by_xpath() # xpath定位find_element_by_css_selector() # css定位

左右滑动查看更多

找到元素之后就是对元素进行操作了:

element.text:获取元素的文本。

element.tag_name:获取标签名称。

element.clear():清除文本。

element.send_keys(value):输入文字或键盘按键。

element.click():单击元素。

element.get_attribute(name):获得属性值

最后,知道了以上selenium方法,它又是如何实现自动化的呢?想想我们自己浏览网页,是不是要从网页的第一条记录开始,一条一条往下,到了一页的末尾,点击翻页继续?这里面就要用到遍历循环!非常简单对不对?学习python时最基础的for循环和while循环就好了。当然这里的简单是要建立在页面内的元素都是有规律的!回到刚刚百度搜索python的那个网址。我们可以发现无论是在第几页,它的“下一页”元素的xpath都是 //*[@id="page"]/a[11] 。而每条搜索结果的元素xpath都为:
//*[@id="1"]/h3/a//*[@id="2"]/h3/a//*[@id="3"]/h3/a...//*[@id="61"]/h3/a//*[@id="62"]/h3/a...
左右滑动查看更多
通过循环遍历这些xpath,然后使用 .click() 的方法就能进入到详情页啦。此外,在一些系统性的网站中,还可通过找到网址url的规律访问到详情页。到这里你就会发现,在熟练了selenium后,接下来要做的就是一个找规律。(详细的循环遍历实例请参考上一期《经生小白》)

踩坑出坑


一号坑:NoSuchElementException 未找到相应元素
当我在用selenium编写代码时经常会遇到这个问题,令人抓狂。因为从程序的逻辑上讲自认为是没有问题的,好不容易写好了一长串代码满心欢喜看着第一页第二页爬取中,突然一个错误,程序终止(原谅小白我try except语句不熟练)。当我以为当程序运行到这个网址就能发现目标Element再进行下一步操作,现实总是给我浇一盆冷水。
例如:
import selenium.webdriver as webdriverfrom selenium.webdriver.common.keys import Keysfrom selenium.webdriver.chrome.options import Options

engine_path = r'C:\Users\Administrator\Desktop\chromedriver_win32.exe' # 插件安装的地址options = webdriver.ChromeOptions() # 可以在这里给谷歌浏览器修改参数

# 低配版请求参数就算搞定了,可以用这个driver虚拟浏览器访问啦,复杂的还可加上请求头设置,ip设置等driver = webdriver.Chrome(executable_path=engine_path, options=options) url = 'https://www.baidu.com/' # 在引号内输入网址driver.get(url) # 开始请求访问

# 找到id为‘kw’(即为搜索框)并在搜索框中输入pythondriver.find_element_by_id("kw").send_keys("python") driver.find_element_by_id("su").click() # 点击搜索driver.find_element_by_xpath('//*[@id="page"]/a[10]').click()  # 在搜索结果中点击下一页
左右滑动查看更多
运行后我们发现NoSuchElementException,报错的位置在上述代码中的最后一行:
driver.find_element_by_xpath('//*[@id="page"]/a[10]').click()

左右滑动查看更多

在经过一系列自闭操作后,我终于明白这是因为代码执行速度很快,但浏览器响应很慢,页面的元素还没有加载完,找不到该元素。在小刘老师的建议下,将原来的错误语句之前加上成这三条:

page_xpath = '//*[@id="page"]/a[10]'bool_func = lambda x: driver.find_elements_by_xpath(page_xpath)

# 在20s内等待找到这一元素为止,超出则报错webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func)

左右滑动查看更多

这样一来,虽然增加了程序的运行时间,但有效的保障了稳定性。
此外NoSuchElementException还可能因为以下原因报出:

1)元素标识使用不正确;

2)素可能在frame标签下。如果是需要写切换到frame中在进行查找(没遇到过);

3)元素属性可能是隐藏的(没遇到过)

4)一般的爬虫程序都是处于重复的自动运行状态,一旦一个网页链接失败,找不到元素,就可能导致下一步程序无法也无法找到相应元素,这时便需要多加一些try语句来处理异常。

(参考https://blog.csdn.net/zhusongziye/article/details/79703730)


二号坑:TimeoutException 超出时间
在为前面一个问题加了WebDriverWait()函数之后,我信心满满再次运行了代码。又有新的错误报出了,这次是伴随这个函数而来的TimeoutException错误。意思是超出了等待时间,20秒内还未找到该元素。一个坑填上了,另一个坑又出现了!不过这个错误稍稍理解一下就可以明白报错的可能原因:

1)前面设定的bool_func定位标识输入错误,确实无该元素;

👉解决办法:检查前面输入的元素定位符的语句。2)页面失效,或者网页元素加载失败;👉解决办法:如果该页面失效,解决的方法就是使用try语句,当遇到网页无法链接的错误时,跳过该条网址,继续进行下一个。当然若是因为IP频繁访问,或者被检测到是爬虫程序而被拒绝访问,解决方法可以选择添加IP代理proxy()、cookies等请求元素。3)自动化运行的程序中出现某个错误,导致当前页面不是在理想的目标页面下,所以也就找不到该元素。

👉解决办法:则需要好好看一下整个程序的运行流程,是否会因为前置的错误影响后续的进程,加—上纠错机制。以下为2)的例子:

import selenium.webdriver as webdriverfrom selenium.webdriver.common.keys import Keysfrom selenium.webdriver.chrome.options import Options
engine_path = r'C:\Users\Administrator\Desktop\chromedriver_win32.exe' options = webdriver.ChromeOptions() driver = webdriver.Chrome(executable_path=engine_path, options=options) url = 'https://www.baidu.com/' driver.get(url) driver.find_element_by_id("kw").send_keys("python")driver.find_element_by_id("su").click()page_xpath = '//*[@id="1"]/h3/a' bool_func = lambda x: driver.find_elements_by_xpath(page_xpath)webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func) driver.find_element_by_xpath('//*[@id="1"]/h3/a').click() # 点击进入搜索结果的第一个网页
page_xpath = '//*[@id="submit"]' # 我在该网页内随便选的一个元素bool_func = lambda x: driver.find_elements_by_xpath(page_xpath)webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func)print('完成该页面爬取')print('第二页')
左右滑动查看更多

由于python的官方网站是一个国外的网址,国内虽然可以访问,但加载速度非常慢,常常会超出时间或直接无法加载。

修改:

try: page_xpath = '//*[@id="submit"]' bool_func = lambda x: driver.find_elements_by_xpath(page_xpath) webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func)except: print('该页面无法加载,跳过进行下一个')else: print('完成该页面爬取')print('第二页')

左右滑动查看更多


三号坑:element not visible 元素不可见
随着项目的进行,又有一类找元素的错误出现了。与第一类错误不同是,并非没有找到element,而是不可见。为什么会不可见呢?

1)元素之间存在逻辑关系:比如你要选择地址时,“中国”选择完毕之后,才能选择“北京”。如果想直接一步到位,则会出现element not visible。这种错误并不是元素定位错误所引起的,而是不符合网页代码逻辑;

👉解决办法:注意逻辑顺序的选取,按照网站的要求激活一个再激活下一个。

2)元素定位错误:如果网页中存在你需要定位的多个元素。如果出现element not visible,有可能是你想要的定位和现实的定位出现了不同;👉解决办法:也就是定位更精确,即定位更有区分性。可以采用By.Id,By.xpath(expression)方式。3)元素定位到一个鼠标事件后才能进行事件触发的位置上:比如,需要鼠标移动到某个区域,元素才可以显示点击的按钮,如果鼠标离开,则相应的事件也没办法触发;

👉解决办法:问题都已经描述清楚了,响应的解决办法也就有了,那就是引入action类,模拟鼠标移动到需要定位的元素下,让dom树从新生成,然后定位相应的元素。

(参考https://www.cnblogs.com/star12111/p/7607943.html)

例如我最近做的项目中,主要遇到了第一种的情况,如下图:

这是中华人民共和国海关里统计月报中板块,我想要爬取2018年美元值的第一张表。虽然这张表有它的xpath可以定位,网页源码中也确实存在该元素,但程序无法一步找到,需要像人操作一样先点“2018”,再点“美元”,才能找到该元素,并执行点击进入操作。

下面展示错误的代码:

import selenium.webdriver as webdriver

engine_path = r'C:\Users\Administrator\Desktop\chromedriver_win32.exe' # 需要修改为自己的chromedriver文件存储路径options = webdriver.ChromeOptions() driver = webdriver.Chrome(executable_path=engine_path, options=options) url = 'http://www.customs.gov.cn/customs/302249/302274/302277/index.html' driver.get(url) page_xpath = '//*[@id="ess_ContentPane"]/table[2]/tbody/tr[2]/td/div[1]/div/div/div[4]/table/tbody/tr[2]/td[2]/a[1]'bool_func = lambda x: driver.find_elements_by_xpath(page_xpath)webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func)driver.find_element_by_xpath('//*[@id="ess_ContentPane"]/table[2]/tbody/tr[2]/td/div[1]/div/div/div[4]/table/tbody/tr[2]/td[2]/a[1]').click()

左右滑动查看更多 


Q

怎么修正这个错误?

A

公众号后台复“防坑指南”,

看正确答


一个总结

虽然说写代码的时候遇到报错是很伤脑筋的事情,有时候解决一个报错甚至要花掉大半个下午的时间(自信心大受打击),但是仔细回顾,解决代码错误也是另外一种提高的办法。问题就在那里,网页的实际情况就在那里。这一次爬虫没有遇到,总有会有遇到的时候,早遇早提高嘛!当然了,不单单是爬虫,写什么代码都会遇到报错,学会解决代码错误、总结各个异常情况的解决办法也是熟练度提升的标志。刚刚学习python的我还是要多练习多总结才是!


►往期推荐

回复【Python】👉 简单有用易上手


回复【学术前沿】👉机器学习丨大数据

回复【数据资源】👉公开数据

回复【可视化】👉 你心心念念的数据呈现

回复【老姚专栏】👉老姚趣谈值得一


►一周热文

工具&方法丨学经济学也要敲代码——经生小白自力更生收集数据

特别推荐丨老姚专栏:经典变量误差与衰减偏误:一个新的案例


数据Seminar

这里是大数据、分析技术与学术研究的三叉路口



作者:天天向尚(金日超)审阅:杨奇明编辑:青酱



    欢迎扫描👇二维码添加关注    


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存