第 6 章 屏幕抓取和其他实用程序
本章攻略:
- 使用谷歌地图API搜索公司地址
- 使用谷歌地图URL搜索地理坐标
- 搜索维基百科中的文章
- 使用谷歌搜索股价
- 搜索GitHub中的源代码仓库
- 读取BBC的新闻订阅源
- 爬取网页中的链接
6.1 简介
本章介绍一些有趣的Python脚本,可让你从网络中获取有用信息,例如公司地址、某公司的股价或通讯社网站中的最新资讯。这些Python脚本演示了不使用复杂的API时,如何以更简单的方式获取简要信息。
按照这些攻略的做法,你能编写适应复杂需求的代码,例如,找到一家公司的详细信息,包括所在地、新闻、股价等。
6.2 使用谷歌地图API搜索公司地址
你可能想搜索所在地区内一家知名公司的地址。
6.2.1 准备工作
你可以使用Python的地理编码库pygeocoder
搜索本地公司。你需要使用pip
或easy_install
从PyPI上安装这个库,输入的命令为$ pip install pygeocoder
或$ easy_install pygeocoder
。
6.2.2 实战演练
我们来使用几行Python代码查找英国著名零售商Argos有限公司的地址。
代码清单6-1是一个简单的地理编码示例,用于搜索一家公司的地址,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 6
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
from pygeocoder import Geocoder
def search_business(business_name):
results = Geocoder.geocode(business_name)
for result in results:
print result
if __name__ == '__main__':
business_name = "Argos Ltd, London"
print "Searching %s" %business_name
search_business(business_name)
这个脚本会打印出Argos有限公司的地址,如下所示。输出的内容根据所安装的地理编码库会有细微的差别。
$ python 6_1_search_business_addr.py
Searching Argos Ltd, London
Argos Ltd, 110-114 King Street, London, Greater London W6 0QP, UK
6.2.3 原理分析
这个攻略依赖于Python的第三方地理编码库。
这个攻略定义了一个简单的函数,search_business()
,其参数是公司名,然后它把公司名传给geocode()
方法。geocode()
方法可能返回零个或多个结果,具体取决于搜索的关键字。
在这个攻略中,传给geocode()
方法的搜索关键字是“Argos Ltd, London”。得到的结果是Argos有限公司的地址,即“110-114 King Street, London, Greater London W6 0QP, UK”。
6.2.4 参考资源
pygeocoder
库很强大,有很多与地理编码有关的有趣和有用的功能。在开发者的网站中有更详细的说明,地址为https://bitbucket.org/xster/pygeocoder/wiki/Home。
6.3 使用谷歌地图URL搜索地理坐标
有时你需要一个简单的函数,只通过城市名即可找出城市的地理坐标。你或许不想为这么简单的任务安装任何第三方库。
6.3.1 实战演练
在这个简单的屏幕抓取示例中,我们使用谷歌地图URL查询一个城市的纬度和经度。在谷歌地图中搜索一次之后就能找到查询所需的URL。我们可以按照下面的步骤从谷歌地图中提取信息。
城市名使用argparse
模块从命令行中获取。
地图搜索URL使用urllib
模块中的urlopen()
函数打开。如果URL正确,会得到一个XML格式的输出。
然后处理XML输出,获取该城市的地理坐标。
代码清单6-2使用谷歌地图查找一个城市的地理坐标,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 6
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import argparse
import os
import urllib
ERROR_STRING = '<error>'
def find_lat_long(city):
""" Find geographic coordinates """
# Encode query string into Google maps URL
url = 'http://maps.google.com/?q=' + urllib.quote(city) + '&output=js'
print 'Query: %s' % (url)
# Get XML location from Google maps
xml = urllib.urlopen(url).read()
if ERROR_STRING in xml:
print '\nGoogle cannot interpret the city.'
return
else:
# Strip lat/long coordinates from XML
lat,lng = 0.0,0.0
center = xml[xml.find('{center')+10:xml.find('}',xml.find('{center'))]
center = center.replace('lat:','').replace('lng:','')
lat,lng = center.split(',')
print "Latitude/Longitude: %s/%s\n" %(lat, lng)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='City Geocode Search')
parser.add_argument('--city', action="store", dest="city", required=True)
given_args = parser.parse_args()
print "Finding geographic coordinates of %s" %given_args.city
find_lat_long(given_args.city)
运行这个脚本后,会看到类似下面的输出:
$ python 6_2_geo_coding_by_google_maps.py --city=London
Finding geograhic coordinates of London
Query: http://maps.google.com/?q=London&output=js
Latitude/Longitude: 51.511214000000002/-0.119824
6.3.2 原理分析
这个攻略从命令行中获取城市名,然后将其传给find_lat_long()
函数。这个函数使用urllib
模块中的urlopen()
函数查询谷歌地图服务,得到XML格式的结果。然后,在得到的结果中搜索字符串'
,如果找不到就说明结果没问题。
如果你把原始的XML打印出来,会发现有很多字符,这些字符是为浏览器生成的。在浏览器中,需要在地图上显示图层。但在这个攻略中,我们只需要纬度和经度。
我们使用字符串处理方法find()
从原始的XML中提取出纬度和经度。我们搜索的关键字是“center”,以便找出地理坐标信息。但得到的结果中包含一些额外的字符,所以又调用replace()
方法将其删除。
你可以使用这个攻略查找世界上任何一座城市的经纬度。
6.4 搜索维基百科中的文章
维基百科是个非常棒的网站,汇聚了几乎所有的信息,例如人物、场所和技术等。如果你想使用Python脚本在维基百科中搜索点儿什么,可以参考这个攻略。
下面是一篇文章示例:
6.4.1 准备工作
你需要使用pip
或easy_install
从PyPI上安装第三方库pyyaml
,输入的命令为$ pip install pyyaml
或$ easy_install pyyaml
。
6.4.2 实战演练
我们使用关键字“Islam”搜索维基百科,然后把结果打印出来,一行显示一个。
代码清单6-3展示了如何在维基百科中搜索文章,如下所示:
#!usrbin/env python
# -*- coding: utf-8 -*-
# Python Network Programming Cookbook -- Chapter - 6
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import argparse
import re
import yaml
import urllib
import urllib2
SEARCH_URL = 'http://%s.wikipedia.org/w/api.php?action=query&list=search&srsearch=%s&sroffset=%d&srlimit=%d&format=yaml'
class Wikipedia:
def __init__(self, lang='en'):
self.lang = lang
def getcontent(self, url):
request = urllib2.Request(url)
request.add_header('User-Agent', 'Mozilla/20.0')
try:
result = urllib2.urlopen(request)
except urllib2.HTTPError, e:
print "HTTP Error:%s" %(e.reason)
except Exception, e:
print "Error occured: %s" %str(e)
return result
def search_content(self, query, page=1, limit=10):
offset = (page - 1) limit
url = SEARCH_URL % (self.lang, urllib.quote_plus(query), offset, limit)
content = self.getcontent(url).read()
parsed = yaml.load(content)
search = parsed['query']['search']
if not search:
return
results = []
for article in search:
snippet = article['snippet']
snippet = re.sub(r'(?m)<.?>', '', snippet)
snippet = re.sub(r'\s+', ' ', snippet)
snippet = snippet.replace(' . ', '. ')
snippet = snippet.replace(' , ', ', ')
snippet = snippet.strip()
results.append({
'title' : article['title'].strip(),
'snippet' : snippet
})
return results
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Wikipedia search')
parser.add_argument('--query', action="store", dest="query", required=True)
given_args = parser.parse_args()
wikipedia = Wikipedia()
search_term = given_args.query
print "Searching Wikipedia for %s" %search_term
results = wikipedia.search_content(search_term)
print "Listing %s search results..." %len(results)
for result in results:
print "==%s== \n \t%s" %(result['title'], result['snippet'])
print "---- End of search results ----"
运行这个脚本在维基百科中搜索“Islam”,得到的输出结果如下所示:
$ python 6_3_search_article_in_wikipedia.py --query='Islam'
Searching Wikipedia for Islam
Listing 10 search results...
==Islam==
Islam. (ˈ|ɪ| s | l |ɑː| m مالسإلا, ar | ALA | al-ʾIslām ælʔɪsˈlæːm | IPA | ar-al_islam. ...
==Sunni Islam==
Sunni Islam (ˈ | s | uː | n | i or ˈ | s | ʊ | n | i |) is the
largest branch of Islam ; its adherents are referred to in Arabic as ...
==Muslim==
A Muslim, also spelled Moslem is an adherent of Islam, a
monotheistic Abrahamic religion based on the Qur'an —which Muslims
consider the ...
==Sharia==
is the moral code and religious law of Islam. Sharia deals with
many topics addressed by secular law, including crime, politics, and ...
==History of Islam==
The history of Islam concerns the Islamic religion and its
adherents, known as Muslim s. " "Muslim" is an Arabic word meaning
"one who ...
==Caliphate==
a successor to Islamic prophet Muhammad ) and all the Prophets
of Islam. The term caliphate is often applied to successions of
Muslim ...
==Islamic fundamentalism==
Islamic ideology and is a group of religious ideologies seen as
advocating a return to the "fundamentals" of Islam : the Quran and
the Sunnah. ...
==Islamic architecture==
Islamic architecture encompasses a wide range of both secular
and religious styles from the foundation of Islam to the present day. ...
---- End of search results ----
6.4.3 原理分析
首先,我们组建了用于搜索文章的维基百科URL模板。然后定义一个名为Wikipedia
的类,其中有两个方法:getcontent()
和search_content()
。默认情况下,初始化时,把语言属性lang
设为en
(英语)。
命令行中输入的查询字符串传给search_content()
方法,替换模板中的语言、查询字符串、偏移页数和返回结果数量,得到真正的搜索URL。search_content()
方法的page
参数是可选的,偏移页数由表达式(page -1) * limit
计算得出。
搜索结果的内容通过getcontent()
方法获得。在getcontent()
方法中调用了urllib
模块中的urlopen()
函数。在搜索URL中,我们把结果的格式设为yaml
,这基本上就是纯文本文件。然后再使用Python的pyyaml
库解析得到的yaml
格式搜索结果。
搜索结果使用正则表达式替换各结果中的内容。例如,re.sub(r'(?m)<.*?>', '', snippet)
在字符串snippet
中替换匹配(?m)<.*?>)
模式的内容。若想进一步学习正则表达式,请阅读Python文档,地址是http://docs.python.org/2/howto/regex.html。
在维基百科中,每篇文章都有一个摘要或简短说明。我们创建了一个由字典组成的列表,列表中的每个元素都包含一个搜索结果的标题和摘要。然后遍历这个由字典组成的列表,打印搜索结果。
6.5 使用谷歌搜索股价
如果你关注某公司的股价,这个攻略能帮助你了解该公司今天的股价。
6.5.1 准备工作
假设你知道所关注的公司在股票交易所挂牌上市使用的代号。如果不知道,可以在该公司的网站中查找,或者在谷歌中搜索。
6.5.2 实战演练
我们要使用谷歌财经(http://finance.google.com/)搜索指定公司的股价。你可以在命令行中输入代号,如下面的代码所示。
代码清单6-4说明如何在谷歌中搜索股价,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 6
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import argparse
import urllib
import re
from datetime import datetime
SEARCH_URL = 'http://finance.google.com/finance?q='
def get_quote(symbol):
content = urllib.urlopen(SEARCH_URL + symbol).read()
m = re.search('id="ref_694653_l".*?>(.*?)<', content)
if m:
quote = m.group(1)
else:
quote = 'No quote available for: ' + symbol
return quote
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Stock quote search')
parser.add_argument('--symbol', action="store", dest="symbol", required=True)
given_args = parser.parse_args()
print "Searching stock quote for symbol '%s'" %given_args.symbol
print "Stock quote for %s at %s: %s" %(given_args.symbol , datetime.today(), get_quote(given_args.symbol))
运行这个脚本后,会看到类似下面的输出。这里,我们输入代号goog
,搜索谷歌的股价,如下所示:
$ python 6_4_google_stock_quote.py --symbol=goog
Searching stock quote for symbol 'goog'
Stock quote for goog at 2013-08-20 18:50:29.483380: 868.86
6.5.3 原理分析
在这个脚本中,使用urllib
模块中的urlopen()
函数从谷歌财经网站中获取股票数据。
我们使用正则表达式库re
,从第一组数据中获取股价。re
库的search
函数很强大,能搜索内容并过滤特定公司的ID。
我们使用这个攻略搜索了谷歌的股价,在2013年8月20日,其股价是868.86。
6.6 搜索GitHub中的源代码仓库
作为一个Python程序员,你可能已经知道GitHub(http://www.github.com,如下面的截图所示)这个源代码分享网站了。使用GitHub,你可以把源代码私下分享给团队,也可以公开分享给全世界。GitHub有一个好用的API接口,可以查询任何源代码仓库。这个攻略或许能为你的源代码搜索引擎提供一些起步代码。
6.6.1 准备工作
运行这个脚本需要安装Python第三方库requests
,执行命令$ pip install requests
或$ easy_install requests
即可。
6.6.2 实战演练
我们要定义search_repository()
函数,其参数是作者名(即程序员名)、仓库名和搜索的键名,得到的是键对应的结果。根据GitHub的API,可用的搜索键有:issues_url
、has_wiki
、forks_url
、mirror_url
、subscription_url
、 notifications_url
、collaborators_url
、updated_at
、private
、pulls_url
、 issue_comment_url
、labels_url
、full_name
、owner
、statuses_url
、 id
、keys_url
、description
、tags_url
、network_count
、downloads_url
、 assignees_url
、contents_url
、git_refs_url
、open_issues_count
、 clone_url
、watchers_count
、git_tags_url
、milestones_url
、 languages_url
、size
、homepage
、fork
、commits_url
、issue_events_url
、 archive_url
、comments_url
、events_url
、contributors_url
、html_url
、 forks
、compare_url
、open_issues
、git_url
、svn_url
、merges_url
、 has_issues
、ssh_url
、blobs_url
、master_branch
、git_commits_url
、 hooks_url
、has_downloads
、watchers
、name
、language
、url
、 created_at
、pushed_at
、forks_count
、default_branch
、teams_url
、 trees_url
、organization
、branches_url
、subscribers_url
和stargazers_url
。
代码清单6-5给出了在GitHub中搜索源代码仓库详情的代码,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 6
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
SEARCH_URL_BASE = 'https://api.github.com/repos'
import argparse
import requests
import json
def search_repository(author, repo, search_for='homepage'):
url = "%s/%s/%s" %(SEARCH_URL_BASE, author, repo)
print "Searching Repo URL: %s" %url
result = requests.get(url)
if(result.ok):
repo_info = json.loads(result.text or result.content)
print "Github repository info for: %s" %repo
result = "No result found!"
keys = []
for key,value in repo_info.iteritems():
if search_for in key:
result = value
return result
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Github search')
parser.add_argument('--author', action="store", dest="author", required=True)
parser.add_argument('--repo', action="store", dest="repo", required=True)
parser.add_argument('--search_for', action="store", dest="search_for", required=True)
given_args = parser.parse_args()
result = search_repository(given_args.author, given_args.repo, given_args.search_for)
if isinstance(result, dict):
print "Got result for '%s'..." %(given_args.search_for)
for key,value in result.iteritems():
print "%s => %s" %(key,value)
else:
print "Got result for %s: %s" %(given_args.search_for, result)
如果运行这个脚本搜索Python Web框架Django的拥有者,得到的结果如下所示:
$ python 6_5_search_code_github.py --author=django --repo=django --search_for=owner
Searching Repo URL: https://api.github.com/repos/django/django
Github repository info for: django
Got result for 'owner'...
following_url => https://api.github.com/users/django/following{/other_user}
events_url => https://api.github.com/users/django/events{/privacy}
organizations_url => https://api.github.com/users/django/orgs
url => https://api.github.com/users/django
gists_url => https://api.github.com/users/django/gists{/gist_id}
html_url => https://github.com/django
subscriptions_url => https://api.github.com/users/django/subscriptions
avatar_url => https://1.gravatar.com/avatar/fd542381031aa84dca86628ece84f
c07?d=https%3A%2F%2Fidenticons.github.com%2Fe94df919e51ae96652259468415d4f77.png
repos_url => https://api.github.com/users/django/repos
received_events_url => https://api.github.com/users/django/received_events
gravatar_id => fd542381031aa84dca86628ece84fc07
starred_url => https://api.github.com/users/django/starred{/owner}{/repo}
login => django
type => Organization
id => 27804
followers_url => https://api.github.com/users/django/followers
6.6.3 原理分析
这个脚本接收三个命令行参数:仓库作者(--author
)、仓库名(--repo
)、要搜索的信息(--search_for
)。这些参数由argparse
模块解析。
在search_repository()
函数中,把这些命令行参数添加到一个固定的搜索URL中,然后调用requests
模块中的get()
方法获取搜索结果。
默认情况下,返回的搜索结果是JSON格式。然后,调用json
模块中的loads()
方法处理搜索结果。在结果中查找搜索的键,把键对应的值返回给search_repository()
函数的调用程序。
在__main__
块中,我们检查搜索结果是否为一个Python字典实例。如果是,就遍历结果,把键值对打印出来;否则,直接打印结果。
6.7 读取BBC的新闻订阅源
如果你在开发一个新闻和故事相关的社会化网站,或许想显示世界上不同新闻通讯社(例如BBC和路透社)的新闻。让我们试着使用Python脚本从BBC读取新闻。
6.7.1 准备工作
这个攻略依赖于Python第三方库feedparser
。你可以执行下面的命令安装这个库:
$ pip install feedparser
或
$ easy_install feedparser
6.7.2 实战演练
首先,我们要从BBC的网站上找到新闻订阅源的URL。这个URL可以作为搜索不同类型新闻的模板,例如国际新闻、国内新闻、健康新闻、商业新闻和技术新闻。新闻的类型可以从用户的输入中获取。然后,调用read_news()
函数,从BBC的网站中读取新闻。
代码清单6-6说明了如何从BBC的新闻订阅源读取新闻,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 6
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
from datetime import datetime
import feedparser
BBC_FEED_URL = 'http://feeds.bbci.co.uk/news/%s/rss.xml'
def read_news(feed_url):
try:
data = feedparser.parse(feed_url)
except Exception, e:
print "Got error: %s" %str(e)
for entry in data.entries:
print(entry.title)
print(entry.link)
print(entry.description)
print("\n")
if __name__ == '__main__':
print "==== Reading technology news feed from bbc.co.uk (%s)====" %datetime.today()
print "Enter the type of news feed: "
print "Available options are: world, uk, health, sci-tech, business, technology"
type = raw_input("News feed type:")
read_news(BBC_FEED_URL %type)
print "==== End of BBC news feed ====="
运行这个脚本后,会显示可选的新闻类别。如果选择技术类,就会显示技术相关的最新新闻,如下面的输出所示:
$ python 6_6_read_bbc_news_feed.py
==== Reading technology news feed from bbc.co.uk (2013-08-20 19:02:33.940014)====
Enter the type of news feed:
Available options are: world, uk, health, sci-tech, business, technology
News feed type:technology
Xbox One courts indie developers
http://www.bbc.co.uk/news/technology-23765453#sa-ns_mchannel=rss&ns_source=PublicRSS20-sa
Microsoft is to give away free Xbox One development kits to encourage
independent developers to self-publish games for its forthcoming console.
Fast in-flight wi-fi by early 2014
http://www.bbc.co.uk/news/technology-23768536#sa-ns_mchannel=rss&ns_source=PublicRSS20-sa
Passengers on planes, trains and ships may soon be able to take advantage
of high-speed wi-fi connections, says Ofcom.
Anonymous 'hacks council website'
http://www.bbc.co.uk/news/uk-england-surrey-23772635#sa-ns_mchannel=rss&ns_source=PublicRSS20-sa
A Surrey council blames hackers Anonymous after references to a Guardian
journalist's partner detained at Heathrow Airport appear on its website.
Amazon.com website goes offline
http://www.bbc.co.uk/news/technology-23762526#sa-ns_mchannel=rss&ns_source=PublicRSS20-sa
Amazon's US website goes offline for about half an hour, the latest high-profile internet firm to face such a problem in recent days.
[TRUNCATED]
6.7.3 原理分析
在这个攻略中,read_news()
函数依赖于Python第三方模块feedparser
。feedparser
模块中的parse()
方法以结构化形式返回订阅源中的数据。
在这个攻略中,parse()
方法解析指定的订阅源URL。这个URL由BBC_FEED_URL
和用户的输入组成。
如果无异常就调用parse()
方法获取订阅源中的数据,再把数据中的内容打印出来,例如每条新闻的标题、链接和描述。
6.8 爬取网页中的链接
有时你可能想在网页中查找某个关键字。在网页浏览器中,可以使用页内搜索功能找到关键字。有些浏览器还能高亮显示关键字。如果情况复杂,你或许还想进一步深入查找,跟踪网页中的每个URL,查找某个关键字。这个攻略的目的是自动化完成这样的任务。
6.8.1 实战演练
我们来定义search_links()
函数,它接收三个参数:搜索的URL、递归搜索的深度、搜索关键字。因为每个URL对应的内容中都可能有很多链接,而且各链接指向的内容中或许还有更多的链接要爬取,所以我们要递归搜索。为了限制递归搜索,我们定义了一个深度。到达指定的深度后,就不会再继续搜索了。
代码清单6-7给出了爬取网页中链接的代码,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 6
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import argparse
import sys
import httplib
import re
processed = []
def search_links(url, depth, search):
# Process http links that are not processed yet
url_is_processed = (url in processed)
if (url.startswith("http://") and (not url_is_processed)):
processed.append(url)
url = host = url.replace("http://", "", 1)
path = ""
urlparts = url.split("")
if (len(urlparts) > 1):
host = urlparts[0]
path = url.replace(host, "", 1)
# Start crawing
print "Crawling URL path:%s%s " %(host, path)
conn = httplib.HTTPConnection(host)
req = conn.request("GET", path)
result = conn.getresponse()
# find the links
contents = result.read()
all_links = re.findall('href="(.*?)"', contents)
if (search in contents):
print "Found " + search + " at " + url
print " ==> %s: processing %s links" %(str(depth), str(len(all_links)))
for href in all_links:
# Find relative urls
if (href.startswith("")):
href = "http:/" + host + href
# Recurse links
if (depth > 0):
search_links(href, depth-1, search)
else:
print "Skipping link: %s ..." %url
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Webpage link crawler')
parser.add_argument('--url', action="store", dest="url", required=True)
parser.add_argument('--query', action="store", dest="query", required=True)
parser.add_argument('--depth', action="store", dest="depth", default=2)
given_args = parser.parse_args()
try:
search_links(given_args.url, given_args.depth,given_args.query)
except KeyboardInterrupt:
print "Aborting search by user request."
如果运行这个脚本在www.python.org中搜索python
,会看到类似下面的输出:
$ python 6_7_python_link_crawler.py --url='http://python.org' --query='python'
Crawling URL path:python.org/
Found python at python.org
==> 2: processing 123 links
Crawling URL path:www.python.org/channews.rdf
Found python at www.python.org/channews.rdf
==> 1: processing 30 links
Crawling URL path:www.python.org/download/releases/3.4.0/
Found python at www.python.org/download/releases/3.4.0/
==> 0: processing 111 links
Skipping link: https://ep2013.europython.eu/blog/2013/05/15/epc20145-call-proposals ...
Crawling URL path:www.python.org/download/releases/3.2.5/
Found python at www.python.org/download/releases/3.2.5/
==> 0: processing 113 links
...
Skipping link: http://www.python.org/download/releases/3.2.4/ ...
Crawling URL path:wiki.python.org/moin/WikiAttack2013
^CAborting search by user request.
6.8.2 原理分析
这个攻略接收三个命令行参数:搜索的URL(--url
)、查询字符串(--query
)、递归深度(--depth
)。这些参数由argparse
模块解析。
把这三个参数传入search_links()
函数后,会递归遍历在指定网页中找到的所有链接。如果运行很长时间还没结束,你应该提前退出脚本。因此,我们才把search_links()
函数放在try-except
块中,以便捕获用户在键盘中输入的中断操作,例如按Ctrl+C键。
search_links()
函数把访问过的链接保存在processed
列表中。processed
放在全局作用域中,以便递归调用函数时使用。
每次搜索时,都要保证只处理HTTP URL,防止出现潜在的SSL验证错误。URL被分成主机和路径两部分。顶层爬取使用httplib
库中的HTTPConnection()
函数实例化。然后发起一个GET
请求,使用正则表达式模块re
处理响应,收集响应中的所有链接。然后在响应中查找要搜索的关键字。如果找到了关键字,就打印出来。
收集到的链接使用相同的方式递归访问。如果找到了相关链接,就在地址前加上http://
,转换成完整的URL。如果搜索深度大于零,说明可以递归搜索,把深度减去一后,再次调用搜索函数。当搜索深度为零时,递归结束。