这篇文章的目的不是为了一步步教新手如何写python爬虫,只是记录自己在学习python爬虫过程中的使用的技术点和遇到的问题,做一个技术沉淀。
常用的工具与技术
Requests库,主要用来做HTTP请求,支持通过参数添加headers、cookie、proxy等,并且支持HTTP请求的许多类型如:PUT,DELETE,HEAD 以及 OPTIONS 等。简单易上手,功能也很强大。当然也可以使用python自带的urllib(python3)或urllib2(python2)。
BeautifulSoup库,主要用来做解析,可以从HTML或XML中提取数据,通过将HTML或XML转变为BeautifulSoup对象,并指定相应的解析器(html.parser,lxml,xml,html5lib),就可以调用BeautifulSoup内置的各种方法,进而从HTML或XML中获取我们想要提取的数据。
正则表达式:当我们使用爬虫爬取数据时,可能大部分时间都在解析数据,我们要想从杂乱的数据中获取我们想要的数据就必须掌握正则表达式,我在这篇文章中有简单的介绍。
Chrome:使用Chrome浏览器的开发者工具可以帮助我们查看网页源代码或检查页面元素,以快速定位我们想要提取的内容;可以查看接口请求信息,包括header和cookie等;还有诸如调式、运行JavaScript代码等很多功能。
Fiddler:Fiddler是通过改写HTTP代理,让数据从它那通过,来监控并且截取到数据,其实日常工作中Chrome开发者工具已经足够应对绝大多数网站,但是像加了瑞数反爬的网站,会限制浏览器的调试功能,这时候Fiddler就派上用场了;此外,抓取APP数据时,Fiddler也是重要的辅助工具。
Postman:Postman在发送网络HTTP请求方面可以说是Chrome插件类产品中的代表产品之一。我们一般使用它来测试接口,包括我们自己的服务接口测试和要爬取的网站的数据接口的测试。
HTML/CSS/JavaScript:爬取网页时,从HMTL代码中提取我们想要的数据时,必须首先要能看懂基本的html代码,才能使用css选择器、xpath等来提取数据,所以必须掌握基本的HTML/CSS知识;但是对于javascript来说,掌握基本语法并不够,很多网站的反爬都是在js上做工作,例如使用js将一些关键参数加密,我们必须具备破解js的能力,才能使我们的爬虫技能进阶。
redis/mongodb:在我的平时工作中,不会将爬取的数据直接存入关系型数据库,一般是将爬取的数据存到mongodb或redis这两类nosql数据库,优点存取方便、快捷;此外,redis还可以用来做爬取条件的中转,这样就可以方便在多台服务器上并行爬取数据,从而实现分布式爬取。
Linux:平时的工作过程中,对linux操作系统的使用也是必须的,至少掌握常用的命令才可以胜任每天的工作,但是要想工作的更高效,则需要深入学习。
PhantomJS:PhantomJS相当于一个无头浏览器,也就是没有界面,但是拥有浏览器的内核,我们可以使用它来做一些web自动化测试、屏幕捕获等,所以我们也可以利用其来爬取数据,并且因为它本身是一个浏览器,所以可以绕过很多反爬,并且可以爬取异步加载的网页,缺点是效率低下、占用资源多。
Selenium:Selenium 是一套完整的web应用程序测试系统,主要是操控各种浏览器来执行一些自动化操作,所以可以将Selenium和PhantomJS结合使用,也就是Selenium操作无头浏览器来爬取数据。
Puppeteer:Puppeteer本质上与Selenium+PhantomJS一样,不过相较Phantom有很多优点,Phantom已经不再有人维护了,并且Selenium也终止了对Phantom的支持,而Puppeteer由Chrome官方团队开发维护,提供接口控制Chromium(Chrome的内核),接口完善,bug少。
常见的反爬策略
- User-Agent:UA 是用户访问网站时候的浏览器标识,一般情况爬虫的UA与正常浏览器的UA不同,所以网站可以根据这一特点封掉爬虫请求。
- Headers:Headers是区分浏览器行为和爬虫行为的最简单的方式,headers中包括UA,但不限于UA;例如也有一部分网站会对Referer进行检测来判断是否为爬虫。
- IP限制:一些网站会根据你的IP 地址访问的频率,次数进行反爬。也就是说如果你用单一的IP 地址访问频率过高,那么服务器会在短时间内禁止这个IP 访问。
- Cookie:Cookie是浏览器访问网站时,网站服务器给浏览器贴的标识,在cookie有效期内浏览器每次访问该网站时都是带着该标识去访问的,从而网站能够快速识别该用户;如果网站对请求的cookie进行检测,而我们的爬虫没有设置cookie时,那么网站就能区分出爬虫。
- 验证码:验证码是反爬的比较古老但非常有效的方式,验证码旨在区分机器行为和人的行为。远古时期,人们觉得机器无法自动识别验证码,但随着机器学习、图像识别等技术的发展,人们已经可以利用这些技术去破解验证码,所以与之验证码的复杂度也不断提升,如增加验证码的干扰线、噪点等,以及后来出现的滑块、点选验证码等。
- 动态加载:从网页的url加载网页源代码之后,会在浏览器里执行Javascript代码,会加载出更多的内容,并把这些内容传输到网页中,所以我们直接请求网页url是无法拿到数据的。
- 网页加密:网页加密一般分为两种,一种是对关键数据加密,使我们无法提取;二是对关键参数加密,使我们无法获取该参数,从而无法成功请求网页。
- 蜜罐技术:在反爬虫的机制中,有一种蜜罐技术。页面上会有意留下某些人类看不见或是一定不会点击的链接。因为爬虫会从源代码中抓取信息,因此爬虫可能会浏览这样的链接。这个时候,只要网站发现了有IP访问这个链接,立刻永久封禁该IP + User-Agent + Mac地址等等能够用来识别访问者身份的所有信息。
- 登录:有些网站需要登录才能爬取,但是我们爬取时的难度不在登录本身,而在于登录后如何实现大批量数据爬取。对于超过一定限制时才需要登陆的网站,我们的首要策略是控制如何不让登录页面跳出来。
应对策略
- User-Agent:构造自己的UA 池,每次请求网页时随机挂上UA 标识,更好地模拟浏览器行为。我们可以手动去搜集各种User-Agent,然后写一个随机函数,每次挑选一个UA去构建请求头;或者直接使用fake_useragent库,专门用于python爬虫伪装。
- Headers:通过审查元素或者开发者工具获取要爬取的网页相应的headers ,然后把headers 添加到代码中,每次携带该headers请求网页。
- IP限制:构造自己的IP 代理池,然后每次访问时随机选择代理。代理池中的IP可以通过抓取网上免费公开的IP获取或者购买。
- Cookie:在首次请求网页时,一般返回的请求头里面会有Set-Cookie,我们只需要将该value值,转换成cookie的格式在下一次请求网页时携带即可,并且每一次请求返回时判断cookie是否更改,若更改我们程序随之更改。
- 验证码:对于连续出错或者访问过于频繁时才会出现验证码的网站,我们要尽量通过程序来控制不让验证码出现;而对于每一次请求必须通过验证码才能请求的网站,那就只能正面硬刚。比较low的方式,将验证码下载到本地,每次手动在程序中输入,适合数据量较少的情况。
- 动态加载:遇到动态加载的网页,首先去寻找它动态加载时调用的API,通过Chrome开发者工具或者Fildder等辅助工具来快速定位,然后使用Postman来测试验证,之后在程序中直接请求该API即可。其次,在我们的爬虫程序中模拟执行浏览器动态加载的javascript程序,这需要对javascript有着比较深入的了解。如果以上两种方式解决不了,接下来考虑PhantomJS/Selenium/Puppeteer等工具。
- 网页加密:对于加密的网页,我们首先需要了解使用何种加密算法,然后模拟加密算法进行还原代码,从而进行接下来的提取工作。如果无法解密,那就考虑PhantomJS/Selenium/Puppeteer等工具。
- 蜜罐技术:定向爬虫的爬行轨迹是由我们来决定的,爬虫会访问哪些网址我们都是知道的。因此即使网站有蜜罐,定向爬虫也未必会中招。
- 登录:通过谷歌的开发者工具或Fiddler,抓取登录包,分析出登录需要传递的数据,然后将用户名和密码等数据通过请求传输给服务器实现登录,然后保存登录后的cookie,后续请求均携带该cookie。有一点需要注意,一旦登录后,每次请求都与我们的账号绑定,不能再随机切换IP等,否则会出现某用户短时间内在各个不同的地区登录的情形,很容易被封号,所以必须严格控制访问频次。若想切换代理,必须同时换用户,所以要想大批量爬取数据,首先需要获取大量的用户名和密码,然后反爬的策略为:使用同一用户和代理IP爬取少量数据,然后切换另一用户和代理IP,这里最好不要等某一用户请求不到数据了再换,这时候就面临着被短暂封号的风险,影响后续的使用,至于请求多少数据更换账号,要根据实际网站来定。
一些经验
同一代理多次重试 :我曾经遇到过一个网站,当挂代理访问时,总是请求不到数据,排除代理质量问题后,发现一个规律:绝大多数情况下,任一IP第一次访问该网站时,会出现访问失败,再次访问就会成功。所以,当我们在编写爬虫时,如果遇到挂代理无法获取数据的情况,排除一些cookie之外的因素,不妨使用同一个代理多次访问试一试。
下载验证码要携带cookie:在我们爬取需要验证码的网站时,一般需要先将验证码识别正确,然后将验证码作为参数或者其他形式发送给服务器,如果下载验证码时携带的cookie与本次请求的cookie不一致,服务器则无法进行匹配,我们将无法请求成功。此外:我们在携带cookie下载验证码时,服务器极有可能还会返回给我们重要cookie用作验证,所以我们这时一般需要合并cookie的操作;更有一些情况(针对具体网站而定),在我们下次请求验证码时需要将上一次请求验证码时服务器给我们的那部分cookie清掉,否则服务器不会返回新的给我们,会导致一些错乱。
多次请求获取cookie:有些网站想要请求到数据,要求我们每次请求时携带cookie,我们可以先请求一次该网站,从返回的响应头中提取cookie值,但是往往要求我们携带的cookie并不会全部包含在响应头中,有可能会隐藏在页面的源代码中或是别的什么位置(这就需要认真观察了),我曾遇到过隐藏在另一个请求的返回值中,也就是我需要请求两个地址各一次,才能获取完整的cookie,从而进行接下来的爬取工作。
前端伪反爬:有的网站前端看起来很高大上,比如要做一次滑动验证才能查看数据,而实际上这个滑动验证是一个纯前端的验证,我们可以直接请求验证之后真正的数据接口来获取数据。
“请求失败”返回200:我们在编写爬虫时,一般会判断每次请求是否返回200,若返回200,进行接下来的提取工作;否则,切换代理或者进行其他一些操作后再次请求。而我遇到过,网站检测出来我是爬虫在工作,并没有直接拒绝我的请求,但是返回的东西并不是我想要的,并且状态码也是200(这里对于我来说是请求失败,但是对于服务器来讲本次请求成功返回,所以返回200没毛病),如果我们不仔细推敲,就很难察觉我们被发现了,这种情况一旦出现,有可能会导致后面大量的的请求都是没有获取到我们想要的数据的,因为返回给我们的状态码一直是200,都没有给我们切换代理的机会。所以,遇到爬虫运行中途突然出现大批量请求不到数据的情况,而程序也没有任何出错提示时,就要来思考这一可能性。
高级反爬:对于比较高深的反爬我目前遇到的并不多,只有“*数”,超复杂的js加密,我们破解不了只能使用Puppeteer来爬取,变态的是,直接使用puppeteer同样访问不了,它还会对浏览器的许多参数来检测是否为爬虫,我遇到的只要修改webdriver即可爬取了,但是不难想到,如果它想提升爬取的难度,对更多的参数进行检测也是有可能的,但是也面临着误判的风险,并且伴随着网站性能下降的代价(因为这些检测全部写在加密的js里,浏览器加载这些js会很耗时)。