【问题标题】:Python Using wildcard inside of stringsPython 在字符串中使用通配符
【发布时间】:2015-02-28 14:56:24
【问题描述】:

我正在尝试从 boxofficemoviemojo.com 中删除数据,并且我已正确设置所有内容。但是,我收到了一个我无法弄清楚的逻辑错误。本质上,我想获取前 100 部电影并将数据写入 csv 文件。

我目前正在使用本站的html进行测试(其他年份相同):http://boxofficemojo.com/yearly/chart/?yr=2014&p=.htm

有很多代码,但这是我正在努力解决的主要部分。代码块如下所示:

def grab_yearly_data(self,page,year):
    # page is the url that was downloaded, year in this case is 2014.

    rank_pattern=r'<td align="center"><font size="2">([0-9,]*?)</font>'
    mov_title_pattern=r'(.htm">[A-Z])*?</a></font></b></td>'
    #mov_title_pattern=r'.htm">*?</a></font></b></td>' # Testing

    self.rank= [g for g in re.findall(rank_pattern,page)]
    self.mov_title=[g for g in re.findall(mov_title_pattern,page)]

self.rank 完美运行。但是 self.mov_title 没有正确存储数据。我想收到一个包含 102 个元素的列表,其中包含电影标题。但是我收到 102 个空字符串:''。一旦我弄清楚我做错了什么,该程序的其余部分将非常简单,我只是无法在网上找到我的问题的答案。我已经尝试更改 mov_title_pattern 很多次,但我要么一无所获,要么收到 102 个空字符串。请帮助我真的很想继续我的项目。

【问题讨论】:

    标签: python html regex web-scraping html-parsing


    【解决方案1】:
    mov_title_pattern=r'.htm">([A-Za-z0-9 ]*)</a></font></b></td>'
    

    试试这个。这应该适用于您的情况。查看演示。

    https://www.regex101.com/r/fG5pZ8/6

    【讨论】:

      【解决方案2】:

      您的正则表达式没有多大意义。它尽可能少地匹配.htm"&gt;[A-Z],通常为零,产生一个空字符串。

      此外,对于这样一个非常通用的正则表达式,不能保证它只匹配结果行。生成的页面包含许多其他地方,您可以在其中找到.htm"&gt;,后面跟着一些东西。

      更一般地说,我会提倡一种方法,您可以制作一个正则表达式来精确识别每个生成的结果行,并从中提取您想要的所有值。换句话说,尝试类似

      re.findall('stuff (rank) stuff (title) stuff stuff stuff')
      

      (我把它作为练习设计一个带有正确 HTML 片段的精确正则表达式,其中我有 stuff 占位符) 并从每个匹配的行中提取“rank”组和“title”组。

      诚然,抓取始终是一项脆弱的业务。如果您使您的正则表达式非常紧凑,那么如果站点更改其布局中的某些细节,它可能会停止工作。如果你让它太放松,它有时会返回错误的东西。

      【讨论】:

        【解决方案3】:

        只需don't attempt to parse HTML with regex - 它可以节省您的时间,最重要的是 - 头发,让您的生活更轻松。

        这是使用BeautifulSoup HTML parser的解决方案:

        from bs4 import BeautifulSoup
        import requests
        
        url = 'http://boxofficemojo.com/yearly/chart/?yr=2014&p=.htm'
        response = requests.get(url)
        
        soup = BeautifulSoup(response.content)
        
        for row in soup.select('div#body td[colspan="3"] > table[border="0"] tr')[1:-3]:
            cells = row.find_all('td')
            if len(cells) < 2:
                continue
        
            rank = cells[0].text
            title = cells[1].text
            print rank, title
        

        打印:

        1 Guardians of the Galaxy
        2 The Hunger Games: Mockingjay - Part 1
        3 Captain America: The Winter Soldier
        4 The LEGO Movie
        ...
        98 Transcendence
        99 The Theory of Everything
        100 As Above/So Below
        

        select() 调用中的表达式是CSS Selector - 一种方便而强大的定位元素的方法。但是,由于这个特定页面上的元素不能方便地用ids 映射或用classes 标记,我们必须依赖像colspan 或border 这样的属性。 [1:-3] slice 在这里是为了消除标题和总行。


        对于this page,要到达表格,您可以依赖图表元素并获得下一个table 兄弟:

        for row in soup.find('div', id='chart_container').find_next_sibling('table').find_all('tr')[1:-3]:
            ...
        

        【讨论】:

        • 我对前 100 部电影 (boxofficemojo.com/movies/…) 的每日数据进行了类似的尝试。出于某种原因,列每周移动 1。关于此语句有什么问题的任何建议?:alltables=soup.findAll("table", {"border":"0", "width":"95%"} ) ??
        • @user3667623 我已经更新了答案,提供了另一种使用数据获取表格的选项-检查一下。希望对您有所帮助。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-31
        • 1970-01-01
        • 2015-07-29
        • 1970-01-01
        • 2018-10-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多