beautifulsoup:可爱的 Python: 使用 mechanize 和 Beautiful Soup 轻松收集 Web 数据

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

  在自动化 Web scrap 或用户和 Web 站点交互模拟中最大困难的就是服务器使用 cookies 跟踪会话进度显然cookies 是 HTTP 头部部分在 urllib 打开资源时会自然显示出来而且标准模块 Cookie(Python 3 中 http.cookie)和 cookielib(Python 3 中 http.cookiejar)有助于在比原始文本处理更高层次上处理这些头部即使如此在这个层次上执行处理也非常繁琐mechanize 库将这种处理提升到个更高程度抽象并使您脚本 — 或交互性 Python shell — 表现出非常类似实际 Web 浏览器行为

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

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

  个实际举例

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

  入门工具

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

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

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

  要了解这些内容我常常使用 Firebug或面向 Firefox Web Developer 插件(或最新 Safari 版本中内置可选 Develop 菜单但是所针对目标人群区别)所有这些工具都可以执行诸如显示表单字段、显示密码、检查页面 DOM、查看或运行 Javascript、观察 Ajax 通信等操作比较这些工具优劣需要另外撰写篇文章但是如果您要进行面向 Web 编程那么必须熟悉这些工具

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

  搜索结果 scraper

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

  检索所有感兴趣结果

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

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

  首先我将检索组页面并将它们保存到磁盘然后执行第 2个任务从这些已保存文件中拉取所需信息当然如果任务涉及使用检索到信息构成同会话内新交互那么您将需要使用顺序稍微区别开发步骤

  因此首先让我们查看我 fetch :


清单 1. 获取页面内容
import sys, time, os 
from mechanize import Browser 
 
LOGIN_URL = 'http://www.example.com/login' 
USERNAME = 'DavidMertz' 
PASSWORD = 'TheSpanishInquisition' 
SEARCH_URL = 'http://www.example.com/search?' 
FIXED_QUERY = 'food=spam&' 'utensil=spork&' 'date=the_future&' 
VARIABLE_QUERY = ['actor=%s' % actor for actor in 
    ('Graham Chapman', 
     'John Cleese', 
     'Terry Gilliam', 
     'Eric Idle', 
     'Terry Jones', 
     'Michael Palin')] 
 
def fetch: 
  result_no = 0         # Number the output files 
  br = Browser        # Create a browser 
  br.open(LOGIN_URL)      # Open the login page 
  br.select_form(name="login") # Find the login form 
  br['username'] = USERNAME   # Set the form values 
  br['password'] = PASSWORD 
  resp = br.submit      # Submit the form 
 
  # Automatic redirect sometimes fails, follow manually when needed 
   'Redirecting' in br.title: 
    resp = br.follow_link(text_regex='click here') 
 
  # Loop through the searches, keeping fixed query parameters 
  for actor in in VARIABLE_QUERY: 
    # I like to watch what's happening in the console 
    pr >> sys.stderr, '***', actor 
    # Lets do the actual query now 
    br.open(SEARCH_URL + FIXED_QUERY + actor) 
    # The query actually gives us links to the content pages we like, 
    # but there are some other links on the page that we ignore 
    nice_links = [l for l in br.links 
             'good_path' in l.url 
            and 'credential' in l.url] 
     not nice_links:    # Maybe the relevant results are empty 
       
    for link in nice_links: 
      try: 
        response = br.follow_link(link) 
        # More console reporting on title of followed link page 
        pr >> sys.stderr, br.title 
        # Increment output filenames, open and write the file 
        result_no  1 
        out = open(result_%04d' % result_no, 'w') 
        pr >> out, response.read 
        out.close 
      # Nothing ever goes perfectly, ignore  we do not get page 
      except mechanize._response.httperror_seek_wrapper: 
        pr >> sys.stderr, "Response error (probably 404)" 
      # Let's not hammer the site too much between fetches 
      time.sleep(1) 


  对感兴趣站点进行交互式研究后我发现我希望执行查询含有些固定元素和些变化元素我仅仅是将这些元素连接成个大 GET 请求并查看 “results” 页面而结果列表包含了我实际需要资源链接因此我访问这些链接(当此过程出现某些会抛出 try/except 块)并保存在这些内容页面上找到任何内容

  很简单是不是?Mechanize 可以做不止这些但是这个简单例子向您展示了 Mechanize 大致功能

  处理结果

  现在我们已经完成了对 mechanize 操作;剩下工作是理解在 fetch 循环期间保存大量 HTML 文件批量处理特性让我能够在个区别中将这些文件整齐、明显地分离开来fetch 和 process 可能交互得更密切Beautul Soup 使得后期处理比初次获取更加简单

  对于这个批处理任务我们希望从获取各种 Web 页面零散内容中生成表式以逗号分隔值(CSV)数据


清单 2. 使用 Beautul Soup 从无序内容中生成整齐数据
from glob import glob 
from BeautulSoup import BeautulSoup 
 
def process: 
  pr "!MOVIE,DIRECTOR,KEY_GRIP,THE_MOOSE" 
  for fname in glob('result_*'): 
    # Put that sloppy HTML o the soup 
    soup = BeautulSoup(open(fname)) 
 
    # Try to find the fields we want, but default to unknown values 
    try: 
      movie = soup.findAll('span', {'':'movie_title'})[1].contents[0] 
    except IndexError: 
      fname = "UNKNOWN" 
 
    try: 
      director = soup.findAll('div', {'':'director'})[1].contents[0] 
    except IndexError: 
      lname = "UNKNOWN" 
 
    try: 
      # Maybe multiple grips listed, key one should be in there 
      grips = soup.findAll('p', {'id':'grip'})[0] 
      grips = " ".join(grips.split)  # Normalize extra spaces 
    except IndexError: 
      title = "UNKNOWN" 
 
    try: 
      # Hide some stuff in the HTML <meta> tags 
      moose = soup.findAll('meta', {'name':'shibboleth'})[0]['content'] 
    except IndexError: 
      moose = "UNKNOWN" 
 
    pr '"%s","%s","%s","%s"' % (movie, director, grips, moose) 




  第次查看 Beautul Soupprocess代码令人印象深刻读者应当阅读有关文档来获得有关这个模块更多细节但是这个代码片段很好地体现了它整体风格大多数 soup 代码包含些对只含有格式良好 HTML 页面 .findAll 这里是些类似 DOM .parent、nextSibling 和 previousSibling 属性它们类似于 Web 浏览器 “quirks” 模式我们在 soup 中找到内容并不完全 是个解析树

  结束语

  诸如我的类守旧者甚至于些更年轻读者都会记住使用 TCL Expect(或使用用 Python 和其他许多语言编写类似内容)编写脚本带来愉悦自动化和 shell 交互包括 telnet、ftp、ssh 等等远程 shell变得非常直观会话中所有内容都被显示出来Web 交互变得更加细致信息被分为头部和内容体并且各种相关资源常常通过 href 链接、框架、Ajax 等被绑定在然而来说您可以 使用 wget 的类工具来检索 Web 服务器提供所有字节然后像使用其他连接协议样运行和 Expect 风格完全相同脚本

  在实战中几乎没有编程人员过分执着于过去老思路方法比如我建议 wget + Expect 思路方法Mechanize 保留了许多和出色 Expect 脚本相同东西令人感觉熟悉和亲切并且和 Expect 样易于编写(如果不是更简单话)Browser 对象命令比如 .select_form、.submit 和 .follow_link是实现 “查找并发送” 操作最简单、最明显思路方法同时绑定了我们希望在 Web 自动化框架中具备复杂状态和会话处理所有优点



Tags:  you'rebeautiful beautifulwebpage mechanizepython beautifulsoup

延伸阅读

最新评论

发表评论