使用Python轻松收集Web站点数据(1)


使用基本的Python模块,可以编写脚本来与Web站点交互,但是如果没有必要的话,那么您就不希望这样做。Python2.x中的模块urllib和urllib2,以及Python3.0中的统一的urllib.*子包,可以在URL的末尾获取资源。然而,当您希望与Web页面中找到的内容进行某种比较复杂的交互时,您需要使用mechanize库。

51CTO推荐专题: Python实用开发指南

在自动化Webscrap或用户与Web站点的交互模拟中,最大的困难之一就是服务器使用cookies跟踪会话进度。显然,cookies是HTTP头部的一部分,在urllib打开资源时会自然显示出来。

即使如此,在这个层次上执行处理也非常的繁琐。mechanize库将这种处理提升到一个更高程度的抽象并使您的脚本—或交互性Pythonshell—表现出非常类似实际Web浏览器的行为。

Python的mechanize受到Perl的WWW:Mechanize的启发,后者具有类似的一组功能。当然,作为长期的Python支持者,我认为mechanize更健壮,它看上去似乎继承了两种语言的通用模式。

mechanize的一个亲密伙伴是同样出色的BeautifulSoup库。这是一个非常神奇的“粗糙的解析器”,用于解析实际Web页面中包含的有效HTML。您不需要将BeautifulSoup用于mechanize,反之亦然,但是多半情况下,当您与“实际存在的Web”交互时,您将希望同时使用这两种工具。

一个实际示例

我曾在多个编程项目中使用过mechanize。最近一个项目是从一个流行的Web站点中收集匹配某种条件的名称的列表。该站点提供了一些搜索工具,但是没有提供任何正式的API来执行此类搜索。虽然访问者可能能够更明确地猜出我过去在做什么,但我将修改给出的代码的细节,以避免暴露有关被scrap的站点或我的客户机的过多信息。一般情况下,我给出的代码对于类似任务是通用的。

入门工具

在实际开发Webscrap/分析代码的过程中,我发现以交互式方式查看、处理和分析Web页面的内容以了解相关Web页面实际发生的操作是非常重要的功能。通常,站点中的一些页面是由查询动态生成但是具有一致的模式),或是根据非常严格的模板预先生成。

完成这种交互式体验的一种重要方法就是在Pythonshell内使用mechanize本身,特别是在一个增强的shell内,比如IPython。通过这种方式,您可以在编写执行希望用于生产中的交互的最终脚本之前,请求各种已链接的资源、提交表单、维护或操作站点cookies,等等。

然而,我发现我与Web站点的许多实验性质的交互在实际的现代Web浏览器中得到了更好的执行。方便地呈现页面可以使您更加快速地了解给定页面或表单中正在发生的事情。问题在于,呈现页面仅仅完成了事情的一半,可能还不到一半。获得“页面源代码”会让您更进一步。要真正理解给定Web页面或与Web服务器的一系列交互的背后的原理,需要了解更多。

要了解这些内容,我常常使用Firebug或面向Firefox的WebDeveloper插件。所有这些工具都可以执行诸如显示表单字段、显示密码、检查页面的DOM、查看或运行Javascript、观察Ajax通信等操作。比较这些工具的优劣需要另外撰写一篇文章,但是如果您要进行面向Web的编程的话,那么必须熟悉这些工具。

不管使用哪一种工具来对准备实现自动化交互的Web站点做实验,您都需要花比编写简洁的mechanize代码用于执行您的任务)更多的时间来了解站点实际发生的行为。

搜索结果scraper

考虑到上面提到的项目的意图,我将把包含100行代码的脚本分为两个功能:

◆检索所有感兴趣的结果

◆从被检索的页面中拉取我感兴趣的信息

使用这种方式组织脚本是为了便于开发;当我开始任务时,我需要知道如何完成这两个功能。我觉得我需要的信息位于一个普通的页面集合中,但是我还没有检查这些页面的具体布局。

首先我将检索一组页面并将它们保存到磁盘,然后执行第二个任务,从这些已保存的文件中拉取所需的信息。当然,如果任务涉及使用检索到的信息构成同一会话内的新交互,那么您将需要使用顺序稍微不同的开发步骤。因此,首先让我们查看我的fetch()函数:

  1. 清单1.获取页面内容  
  2. importsys,time,os  
  3. frommechanizeimportBrowser  
  4.  
  5. LOGIN_URL='http://www.example.com/login' 
  6. USERNAME='DavidMertz' 
  7. PASSWORD='TheSpanishInquisition' 
  8. SEARCH_URL='http://www.example.com/search?' 
  9. FIXED_QUERY='food=spam&''utensil=spork&''date=the_future&' 
  10. VARIABLE_QUERY=['actor=%s'%actorforactorin  
  11. ('GrahamChapman',  
  12. 'JohnCleese',  
  13. 'TerryGilliam',  
  14. 'EricIdle',  
  15. 'TerryJones',  
  16. 'MichaelPalin')]  
  17.  
  18. deffetch():  
  19. result_no=0#Numbertheoutputfiles  
  20. br=Browser()#Createabrowser  
  21. br.open(LOGIN_URL)#Opentheloginpage  
  22. br.select_form(name="login")#Findtheloginform  
  23. br['username']=USERNAME#Settheformvalues  
  24. br['password']=PASSWORD  
  25. resp=br.submit()#Submittheform 

  1. #Automaticredirectsometimesfails,followmanuallywhenneeded  
  2. if'Redirecting'inbr.title():  
  3. resp=br.follow_link(text_regex='clickhere')  
  4.  
  5. #Loopthroughthesearches,keepingfixedqueryparameters  
  6. foractorininVARIABLE_QUERY:  
  7. #Iliketowatchwhat'shappeningintheconsole  
  8. print>>sys.stderr,'***',actor  
  9. #Letsdotheactualquerynow  
  10. br.open(SEARCH_URL+FIXED_QUERY+actor)  
  11. #Thequeryactuallygivesuslinkstothecontentpageswelike,  
  12. #buttherearesomeotherlinksonthepagethatweignore  
  13. nice_links=[lforlinbr.links()  
  14. if'good_path'inl.url  
  15. and'credential'inl.url]  
  16. ifnotnice_links:#Maybetherelevantresultsareempty  
  17. break  
  18. forlinkinnice_links:  
  19. try:  
  20. response=br.follow_link(link)  
  21. #Moreconsolereportingontitleoffollowedlinkpage  
  22. print>>sys.stderr,br.title()  
  23. #Incrementoutputfilenames,openandwritethefile  
  24. result_no+=1  
  25. out=open(result_%04d'%result_no,'w')  
  26. print>>out,response.read()  
  27. out.close()  
  28. #Nothingevergoesperfectly,ignoreifwedonotgetpage  
  29. exceptmechanize._response.httperror_seek_wrapper:  
  30. print>>sys.stderr,"Responseerror(probably404)"  
  31. #Let'snothammerthesitetoomuchbetweenfetches  
  32. time.sleep(1) 

对感兴趣的站点进行交互式研究后,我发现我希望执行的查询含有一些固定的元素和一些变化的元素。我仅仅是将这些元素连接成一个大的GET请求并查看“results”页面。而结果列表包含了我实际需要的资源的链接。

因此,我访问这些链接当此过程出现某些错误时,会抛出try/except块)并保存在这些内容页面上找到的任何内容。很简单,是不是?Mechanize可以做的不止这些,但是这个简单的例子向您展示了Mechanize的大致功能。


评论关闭