​在本文中,我们将分析几个真实网站,来看看我们在《用Python写网络爬虫(第2版)》中学过的这些技巧是如何应用的。首先我们使用Google演示一个真实的搜索表单,然后是依赖JavaScript和API的网站Facebook,接下来是典型的在线商店Gap。由于这些都是活跃的网站,因此读者在阅读本书时这些网站存在已经发生变更的风险。

[德] 凯瑟琳,雅姆尔 著

不过这样也好,因为本文示例的目的是为了向你展示如何应用前面所学的技术,而不是展示如何抓取任何网站。当你选择运行某个示例时,首先需要检查网站结构在示例编写后是否发生过改变,以及当前该网站的条款与条件是否禁止了爬虫。

在本文中,我们将介绍如下主题:

抓取Google搜索结果网页;

调研Facebook的API;

在Gap网站中使用多线程;

​“Google搜索引擎”

为了了解我们对CSS选择器知识的使用情况,我们将会抓取Google的搜索结果。根据中Alexa的数据,Google是全世界最流行的网站之一,而且非常方便的是,该网站结构简单,易于抓取。

图1.1所示为Google搜索主页使用浏览器工具加载查看表单元素时的界面。

​图1.1

可以看到,搜索查询存储在输入参数q当中,然后表单提交到action属性设定的/search路径。我们可以通过将test、作为搜索条件提交给表单对其进行测试,此时会跳转到类似https://www.google.ro/?gws_ rd=cr,ssl&ei=TuXYWJXqBsGsswHO8YiQAQ#q=test&*的 URL中。确切的URL取决于你的浏览器和地理位置。此外,如果开启了Google实时,那么搜索结果会使用AJAX执行动态加载,而不再需要提交表单。虽然URL中包含了很多参数,但是只有用于查询的参数q是必需的。

可以看到,搜索查询存储在输入参数q当中,然后表单提交到action属性设定的/search路径。我们可以通过将test作为搜索条件提交给表单对其进行测试,此时会跳转到类似https://www.google.ro/?gws_ rd=cr,ssl&ei=TuXYWJXqBsGsswHO8YiQAQ#q=test&*的URL中。确切的URL取决于你的浏览器和地理位置。此外,如果开启了Google实时,那么搜索结果会使用AJAX执行动态加载,而不再需要提交表单。虽然URL中包含了很多参数,但是只有用于查询的参数q是必需的。

​图1.2

搜索结果的结构可以使用浏览器工具来查看,如图1.3所示。

​图1.3

从图1.3中可以看出,搜索结果是以链接的形式出现的,并且其父元素是class为"r"的

标签。

想要抓取搜索结果,我们可以使用第2章中介绍的CSS选择器。

1>>> from lxml.html import fromstring

2>>> import requests

4>>> tree = fromstring(html.content)

5>>> results = tree.cssselect('h3.r a')

6>>> results

7,

8 ,

9 ,

10 ,

11 ,

12 ,

13 ,

14 ,

15 ,

16

到目前为止,我们已经下载得到了Google的搜索结果,并且使用lxml抽取出其中的链接。在图1.3中,我们发现链接中的真实网站URL之后还包含了一串附加参数,这些参数将用于跟踪点击。

下面是我们在页面中找到的第一个链接。

1>>> link = results[0].get('href')

2>>> link

4 AFQjCNGXsvN-v4izEgZFzfkIvg'

这里我们需要的内容是http://www.speedtest.net/,可以使用urlparse模块从查询字符串中将其解析出来。

1>>> from urllib.parse import parse_qs, urlparse

2>>> qs = urlparse(link).query

3>>> parsed_qs = parse_qs(qs)

4>>> parsed_qs

6 'sa': ['U'],

7 'ved': ['0ahUKEwiCqMHNuvbSAhXD6gTMAA'],

8 'usg': ['AFQjCNGXsvN-v4izEgZFzfkIvg']}

9>>> parsed_qs.get('q', [])

该查询字符串解析方法可以用于抽取所有链接。

1>>> links = []

2>>> for result in results:

3... link = result.get('href')

4... qs = urlparse(link).query

5... links.extend(parse_qs(qs).get('q', []))

6...

7>>> links

9 'test',

16 e-4th-test-dharamsala-day-3/story-8K124GMEBoiKOgiAaaB5bN.html',

18 s-australia-4th-test-day-3-dharamsala-1673771',

成功了!从Google搜索中得到的链接已经被成功抓取出来了。该示例的完整源码位于本书源码文件的chp9文件夹中,其名为scrape_google.py。

抓取Google搜索结果时会碰到的一个难点是,如果你的IP出现可疑行为,比如下载速度过快,则会出现验证码图像,如图1.4所示。

我们可以降低下载速度,或者在必须高速下载时使用代理,以避免被Google怀疑。过分请求Google会造成你的IP甚至是一个IP段被封禁,几个小时甚至几天无法访问Google的域名,所以请确保你能够礼貌地使用该网站,不会使你的家庭或办公室中的其他人(包括你自己)被列入黑名单。

​图1.4

“Facebook”

为了演示浏览器和API的使用,我们将会研究Facebook的网站。目前,从月活用户数维度来看,Facebook是世界上最大的社交网络之一,因此其用户数据非常有价值。

1.2.1 网站

图1.5所示为Packt出版社的Facebook页面。

当你查看该页的源代码时,可以找到最开始的几篇日志,但是后面的日志只有在浏览器滚动时才会通过AJAX加载。另外,Facebook还提供了一个移动端界面,正如第1章所述,这种形式的界面通常更容易抓取。该页面在移动端的展示形式如图1.6所示。

​图1.5

​图1.6

当我们与移动端网站进行交互,并使用浏览器工具查看时,会发现该界面使用了和之前相似的结构来处理AJAX事件,因此该方法无法简化抓取。虽然这些AJAX事件可以被逆向工程,但是不同类型的Facebook页面使用了不同的AJAX调用,而且依据我的过往经验,Facebook经常会变更这些调用的结构,所以抓取这些页面需要持续维护。因此,如第5章所述,除非性能十分重要,否则最好使用浏览器渲染引擎执行JavaScript事件,然后访问生成的HTML页面。

下面的代码片段使用Selenium自动化登录Facebook,并跳转到给定页面的URL。

1 from selenium import webdriver

2

3 def get_driver():

4 try:

5 return webdriver.PhantomJS()

6 except:

7 return webdriver.Firefox()

8

9 def facebook(username, password, url):

10 driver = get_driver()

12 driver.find_element_by_id('email').send_keys(username)

13 driver.find_element_by_id('pass').send_keys(password)

14 driver.find_element_by_id('loginbutton').submit()

15 driver.implicitly_wait(30)

16 # wait until the search box is available,

17 # which means it has successfully logged in

18 search = driver.find_element_by_name('q')

19 # now logged in so can go to the page of interest

20 driver.get(url)

21 # add code to scrape data of interest here ...

然后,可以调用该函数加载你感兴趣的Facebook页面,并使用合法的Facebook邮箱和密码,抓取生成的HTML页面。

1.2.2 Facebook API

抓取网站是在其数据没有给出结构化格式时的最末之选。而Facebook确实为绝大多数公共或私有(通过你的用户账号)数据提供了API,因此我们需要在构建加强的浏览器抓取之前,首先检查一下这些API提供的访问是否已经能够满足需求。

首先要做的事情是确定通过API哪些数据是可用的。为了解决该问题,我们需要先查阅其API文档。开发者文档的网址为https://developers.facebook.com/docs,在这里给出了所有不同类型的API,包括图谱API,该API中包含了我们想要的信息。如果你需要构建与Facebook的其他交互(通过API或SDK),可以随时查阅该文档,该文档会定期更新并且易于使用。

此外,根据文档链接,我们还可以使用浏览器内的图谱API探索工具,其地址为https://developers.facebook.com/tools/explorer/。如图1.7所示,探索工具是用来测试查询及其结果的很好的地方。

​图1.7

在这里,我可以搜索API,获取PacktPub的Facebook页面ID。图谱探索工具还可以用来生成访问口令,我们可以用它来定位API。

想要在Python中使用图谱API,我们需要使用具有更高级请求的特殊访问口令。幸运的是,有一个名为facebook-sdk(https://facebook-sdk.readthedocs.io)的维护良好的库可以供我们使用。我们只需通过pip安装它即可。

1 pip install facebook-sdk

下面是使用Facebook的图谱API从Packt出版社页面中抽取数据的代码示例。

1 In [1]: from facebook import GraphAPI

2

3 In [2]: access_token = '....' # insert your actual token here

4

5 In [3]: graph = GraphAPI(access_token=access_token, version='2.7')

6

7 In [4]: graph.get_object('PacktPub')

8 Out[4]: {'id': '204603129458', 'name': 'Packt'}

我们可以看到和基于浏览器的图谱探索工具相同的结果。我们可以通过传递想要抽取的额外信息,来获得页面中的更多信息。要确定使用哪些信息,我们可以在图谱文档中看到页面中所有可用的字段,文档地址为https://developers.facebook.com/docs/graph-api/reference/page/。使用关键字参数fields,我们可以从API中抽取这些额外可用的字段。

1 In [5]: graph.get_object('PacktPub', fields='about,events,feed,picture')

2 Out[5]:

3 'about': 'Packt provides software learning resources, from eBooks to video

4 courses, to everyone from web developers to data scientists.',

5 'feed': {'data': [{'created_time': '2017-03-27T10:30:00+0000',

6 'id': '204603129458_10155195603119459',

7 'message': "We've teamed up with CBR Online to give you a chance to win 5

8 tech eBooks - enter by March 31! http://bit.ly/2mTvmeA"},

9...

10 'id': '204603129458',

11 'picture': {'data': {'is_silhouette': False,

12 'url':

14 2357248532027065_n.png?oh=d0a26e6c8a00cf7e6ce957ed2065e430&oe=59660265'}}}

我们可以看到该响应是格式良好的Python字典,我们可以很容易地进行解析。

图谱API还提供了很多访问用户数据的其他调用,其文档可以从Facebook的开发者页面中获取,网址为https://developers.facebook.com/docs/graph-api。根据所需数据的不同,你可能还需要创建一个Facebook开发者应用,从而获得可用时间更长的访问口令。

“Gap”

为了演示使用网站地图查看内容,我们将使用Gap的网站。

Gap拥有一个结构化良好的网站,通过Sitemap可以帮助网络爬虫定位其最新的内容。如果我们使用第1章中学到的技术调研该网站,则会发现在http://www.gap.com/robots.txt这一网址下的robots.txt文件中包含了网站地图的链接。

下面是链接的Sitemap文件中的内容。

1<?xml version="1.0" encoding="UTF-8"?>

3

5 2017-03-24

6

7

9 2017-03-24

10

11

如上所示,Sitemap链接中的内容不仅仅是索引,其中又包含了其他Sitemap文件的链接。这些其他的Sitemap文件中则包含了数千种产品类目的链接,比如http://www.gap.com/products/womens-jogger- pants.jsp,如图1.8所示。

图1.8

这里有大量需要爬取的内容,因此我们将使用第4章中开发的多线程爬虫。你可能还记得该爬虫支持URL模式以匹配页面。我们同样可以定义一个scraper_callback关键字参数变量,可以让我们解析更多链接。

下面是爬取Gap网站中Sitemap链接的示例回调函数。

1 from lxml import etree

2 from threaded_crawler import threaded_crawler

3

4 def scrape_callback(url, html):

5 if url.endswith('.xml'):

6 # Parse the sitemap XML file

7 tree = etree.fromstring(html)

8 links = [e[0].text for e in tree]

9 return links

10 else:

11 # Add scraping code here

12 pass

该回调函数首先检查下载到的URL的扩展名。如果扩展名为.xml,则认为下载到的URL是Sitemap文件,然后使用lxml的etree模块解析XML文件并从中抽取链接。否则,认为这是一个类目URL,不过本例中还没有实现抓取类目的功能。现在,我们可以在多线程爬虫中使用该回调函数来爬取http://gap.com了。

1 In [1]: from chp9.gap_scraper_callback import scrape_callback

2

3 In [2]: from chp4.threaded_crawler import threaded_crawler

4

6

7 In [4]: threaded_crawler(sitemap, '[http://gap.com]*',

8 scraper_callback=scrape_callback)

9 10

10 []

11 Exception in thread Thread-517:

12 Traceback (most recent call last):

13 ...

14 File "src/lxml/parser.pxi", line 1843, in lxml.etree._parseMemoryDocument

15 (src/lxml/lxml.etree.c:118282)

16 ValueError: Unicode strings with encoding declaration are not supported.

17 Please use bytes input or XML fragments without declaration.

不幸的是,lxml期望加载来自字节或XML片段的内容,而我们存储的是Unicode的响应(因为这样可以让我们使用正则表达式进行解析,并且可以更容易地存储到磁盘中)。不过,我们依然可以在本函数中访问该URL。虽然效率不高,但是我们可以再次加载页面;如果我们只对XML页面执行该操作,则可以减少请求的数量,从而不会增加太多加载时间。当然,如果我们使用了缓存的话,也可以提高效率。

下面我们将重写回调函数。

1 import requests

2

3 def scrape_callback(url, html):

4 if url.endswith('.xml'):

5 # Parse the sitemap XML file

6 resp = requests.get(url)

7 tree = etree.fromstring(resp.content)

8 links = [e[0].text for e in tree]

9 return links

10 else:

11 # Add scraping code here

12 pass

现在,如果我们再次尝试运行,可以看到执行成功。

1 In [4]: threaded_crawler(sitemap, '[http://gap.com]*',

2 scraper_callback=scrape_callback)

3 10

4 []

9 Skipping

11 to depth Skipping

13 Skipping

15 due to depth Skipping

17 due to depth ...

和预期一致,Sitemap文件首先被下载,然后是服装类目。在网络爬虫项目中,你会发现自己可能需要修改及调整代码和类,以适应新的问题。这只是从互联网上抓取内容时诸多令人兴奋的挑战之一。

本文摘自《用Python写网络爬虫(第2版)》

[德] 凯瑟琳,雅姆尔 著

史上首本Python网络爬虫图书全新升级,针对Python 3.x编写,提供示例完整源码和实例网站搭建源码。

讲解了如何使用Python来编写网络爬虫程序,内容包括网络爬虫简介,从页面中抓取数据的3种方法,提取缓存中的数据,使用多个线程和进程进行并发抓取,抓取动态页面中的内容,与表单进行交互,处理页面中的验证码问题,以及使用Scarpy和Portia进行数据抓取,并在最后介绍了使用本书讲解的数据抓取技术对几个真实的网站进行抓取的实例,旨在帮助读者活学活用书中介绍的技术。

小福利

关注【异步社区】服务号,转发本文至朋友圈或 50 人以上微信群,截图发送至异步社区服务号后台,并在文章底下留言你对python爬虫或者试读本书感受,我们将选出3名读者赠送《用Python写网络爬虫(第2版)》1本,赶快积极参与吧!(参与活动直达微信端Python爬虫技巧(文末福利))

活动截止时间:2018年8月2日

扫码关注我们

在“异步社区”后台回复“关注”,即可免费获得2000门在线视频课程

阅读原文,购买《用Python写网络爬虫》

python爬虫难点_Python爬虫技巧相关推荐

  1. 花一千多学python值吗_Python爬虫应该怎么学?程序猿花了一周整理的学习技巧,请收下...

    原标题:Python爬虫应该怎么学?程序猿花了一周整理的学习技巧,请收下 Python爬虫为什么受欢迎 如果你仔细观察,就不难发现,懂爬虫.学习爬虫的人越来越多,一方面,互联网可以获取的数据越来越多, ...

  2. python名单查询_Python爬虫实现全国失信被执行人名单查询功能示例

    本文实例讲述了python爬虫实现全国失信被执行人名单查询功能.分享给大家供大家参考,具体如下: 一.需求说明 利用百度的接口,实现一个全国失信被执行人名单查询功能.输入姓名,查询是否在全国失信被执行 ...

  3. python流行的爬虫框架_Python爬虫相关框架

    Python爬虫相关框架,Python的爬虫框架就是一些爬虫项目的半成品.比如我们可以将一些常见爬虫功能的实现代码写好,然后留下一些接口,在做不同的爬虫项目时,我们只需要根据实际情况,只需要写少量需要 ...

  4. python官网学习爬虫资料_Python爬虫学习?

    1 爬虫是互联网上最常见的一种东西了吧. 爬虫这东西每天都在网上爬大量的信息,各大搜索引擎厂商每天都有上百万的爬虫在网络上活动,这些爬虫的作用就是给搜索引擎采集互联网上最新的内容,采集来的内容经过分类 ...

  5. python数据入库_python爬虫(中)--数据建模与保存(入库)

    前言 前面,讲的是提取出来的数据保存进一个extracted_data,再保存进extracted_data_,变成一个list包含list的情况,当然你只提取一项,那就没有必要这么做了,可是我的项目 ...

  6. python电影爬虫背景介绍_python爬虫-爬虫电影八佰词云

    #数据获取 importrequestsimportreimportcsvimportjiebaimportwordcloud#通过循环实现多页爬虫#观察页面链接规律#https://movie.do ...

  7. python爬虫代理服务器_Python爬虫之服务器:代理IP万能

    最近很多同学租服务器用来学习爬虫,对于大部分小白来说,爬虫非常复杂.技术门槛很高.但我们可以通过爬虫获取大量的价值数据,经分析可以发挥巨大的价值,比如:豆瓣.知乎,爬取优质答案,筛选出各话题下热门内容 ...

  8. python爬虫要点_Python爬虫知识点梳理

    学任何一门技术,都应该带着目标去学习,目标就像一座灯塔,指引你前进,很多人学着学着就学放弃了,很大部分原因是没有明确目标,所以,在你准备学爬虫前,先问问自己为什么要学习爬虫.有些人是为了一份工作,有些 ...

  9. python简单爬虫手机号_Python爬虫:大家用公共的手机号干了啥?

    说明:本文所提供的思路和代码都只用于个人测试研究之用,并未对目标网站造成实质性干扰,而且全部细节已经全部告知网站开发者,也请大家不要用于恶意用途. 在我的微信公众号"免费的临时手机号,用这些 ...

最新文章

  1. 我的世界java版forge放在哪_我的世界1.9forge安装 怎么装forge教程
  2. Codeforces Round #281 (Div. 2) C. Vasya and Basketball 二分
  3. 如何设置MongoDB快捷启动?
  4. 【转载】消息队列RabbitMQ入门介绍
  5. H3C实验H3CTE讲师京东翰林分享实验4 WLAN基本配置
  6. 【SpringBoot】SpringBoot之Bean之自动加载
  7. 盘古分词工具学习笔记
  8. 实验一 DOS命令解释程序的编写
  9. Java实现人力资源管理系统
  10. php 中文 验证码,PHP中文验证码函数
  11. 2021福建计算机会考操作题,2021年度福建省信息技术会考笔试试题答案.doc
  12. 迅雷上如何下载热映的电影大片~~
  13. LinuxDNS域名解析服务
  14. 如何在Windows 10上禁用登录屏幕的背景模糊
  15. dhtml、html、html5、xml、xhtml的区别
  16. 《C语言程序设计》第4版 何钦铭、颜晖主编 课后习题答案
  17. Ipad上选择专业好用的思维导图软件
  18. Python脚本25:将两张图片拼在一起
  19. winForm在多显示器(主显示器 + 扩展显示器)上显示最大化和还原操作。
  20. 助力阿米巴经营,实现数字化转型——普元阿米加系统架构与实践

热门文章

  1. Word 2016 撰写论文(5): MathType 矩阵中的每个元素居中对齐
  2. oracle 等待的进程,Oracle 等待事件:ges generic event
  3. 信号与系统实验 01 - | 连续系统频域分析的MATLAB实现
  4. theme vscode 护眼_vscode设置护眼主题
  5. QQ音乐推荐召回算法的探索与实践
  6. 谈NANDnbsp;Flash的底层结构和解析
  7. VB小游戏设计(一):扫雷
  8. 电脑没有声音了怎么恢复?恢复声音的6个方法【图解】
  9. server 2008 文件服务器,windows server 2008文件服务器
  10. PhotoZoom Pro 7 支持哪些图像格式?