【问题标题】:Python->Beautifulsoup->Webscraping->Looping over URL (1 to 53) and saving ResultsPython->Beautifulsoup->Webscraping->遍历 URL(1 到 53)并保存结果
【发布时间】:2016-08-11 12:50:07
【问题描述】:

Here is the Website I am trying to scrape http://livingwage.mit.edu/

具体网址来自

http://livingwage.mit.edu/states/01

http://livingwage.mit.edu/states/02

http://livingwage.mit.edu/states/04 (For some reason they skipped 03)

...all the way to...

http://livingwage.mit.edu/states/56

在这些 URL 中的每一个上,我都需要第二个表的最后一行:

http://livingwage.mit.edu/states/01 的示例

要求的税前年收入 $20,260 $42,786 $51,642 $64,767 $34,325 $42,305 $47,345 $53,206 $34,325 $47,691 56,934 美元 66,997 美元

期望输出:

阿拉巴马州 $20,260 $42,786 $51,642 $64,767 $34,325 $42,305 $47,345 $53,206 $34,325 $47,691 $56,934 $66,997

阿拉斯加 $24,070 $49,295 $60,933 $79,871 $38,561 $47,136 $52,233 $61,531 $38,561 $54,433 $66,316 $82,403

...

...

怀俄明州 $20,867 $42,689 $52,007 $65,892 $34,988 $41,887 $46,983 $53,549 $34,988 $47,826 $57,391 $68,424

经过2个小时的折腾,这是我目前所拥有的(我是初学者):

import requests, bs4

res = requests.get('http://livingwage.mit.edu/states/01')

res.raise_for_status()
states = bs4.BeautifulSoup(res.text)


state_name=states.select('h1')

table = states.find_all('table')[1]
rows = table.find_all('tr', 'odd')[4:]


result=[]

result.append(state_name)
result.append(rows)

当我在 Python 控制台中查看 state_name 和 rows 时,它给了我 html 元素

[<h1>Living Wag...Alabama</h1>]

[<tr class = "odd...   </td> </tr>]

问题 1:这些是我想要的输出中的内容,但是我怎样才能让 python 以字符串格式而不是像上面的 HTML 格式给我呢?

问题 2:如何循环通过 request.get(url01 到 url56)?

感谢您的帮助。

如果你能提供一种更有效的方式来获取我的代码中的 rows 变量,我将不胜感激,因为我到达那里的方式不是很 Pythonic。

【问题讨论】:

    标签: python web-scraping beautifulsoup


    【解决方案1】:

    只需从初始页面获取所有状态,然后您可以选择第二个表并使用 css classes odd results 获取 tr em> 你需要,因为类名是唯一的,所以不需要切片:

    import requests
    from bs4 import BeautifulSoup
    from urllib.parse import  urljoin # python2 -> from urlparse import urljoin 
    
    
    base = "http://livingwage.mit.edu"
    res = requests.get(base)
    
    res.raise_for_status()
    states = []
    # Get all state urls and state name from the anchor tags on the base page.
    # td + td skips the first td which is *Required annual income before taxes*
    # get all the anchors inside each li that are children of the
    # ul with the css class  "states list".
    for a in BeautifulSoup(res.text, "html.parser").select("ul.states.list-unstyled li a"):
        # The hrefs look like "/states/51/locations".
        #  We want everything before /locations so we split on / from the right -> /states/51/
        # and join to the base url. The anchor text also holds the state name,
        # so we return the full url and the state, i.e "http://livingwage.mit.edu/states/01 "Alabama".
        states.append((urljoin(base, a["href"].rsplit("/", 1)[0]), a.text))
    
    
    def parse(soup):
        # Get the second table, indexing in css starts at 1, so table:nth-of-type(2)" gets the second table.
        table = soup.select_one("table:nth-of-type(2)")
        # To get the text, we just need find all the tds and call .text on each.
        #  Each td we want has the css class "odd results", td + td starts from the second as we don't want the first.
        return [td.text.strip() for td in table.select_one("tr.odd.results").select("td + td")]
    
    
    # Unpack the url and state from each tuple in our states list. 
    for url, state in states:
        soup = BeautifulSoup(requests.get(url).content, "html.parser")
        print(state, parse(soup))
    

    如果你运行代码,你会看到如下输出:

    Alabama ['$21,144', '$43,213', '$53,468', '$67,788', '$34,783', '$41,847', '$46,876', '$52,531', '$34,783', '$48,108', '$58,748', '$70,014']
    Alaska ['$24,070', '$49,295', '$60,933', '$79,871', '$38,561', '$47,136', '$52,233', '$61,531', '$38,561', '$54,433', '$66,316', '$82,403']
    Arizona ['$21,587', '$47,153', '$59,462', '$78,112', '$36,332', '$44,913', '$50,200', '$58,615', '$36,332', '$52,483', '$65,047', '$80,739']
    Arkansas ['$19,765', '$41,000', '$50,887', '$65,091', '$33,351', '$40,337', '$45,445', '$51,377', '$33,351', '$45,976', '$56,257', '$67,354']
    California ['$26,249', '$55,810', '$64,262', '$81,451', '$42,433', '$52,529', '$57,986', '$68,826', '$42,433', '$61,328', '$70,088', '$84,192']
    Colorado ['$23,573', '$51,936', '$61,989', '$79,343', '$38,805', '$47,627', '$52,932', '$62,313', '$38,805', '$57,283', '$67,593', '$81,978']
    Connecticut ['$25,215', '$54,932', '$64,882', '$80,020', '$39,636', '$48,787', '$53,857', '$61,074', '$39,636', '$60,074', '$70,267', '$82,606']
    

    您可以在 1-53 的范围内循环,但从基本页面中提取锚点还可以在一个步骤中为我们提供州名称,使用该页面中的 h1 还可以为您提供输出 Living Wage Calculation for阿拉巴马州 然后您必须尝试解析以获取名称,考虑到某些州有更多的单字名称,这将不是微不足道的。

    【讨论】:

    • 非常感谢,这正是我所需要的。现在,如何将此标记为我的问题的已接受答案?
    • @OmiSlash。不用担心,我看到你想通了。
    • 过去几天我一直在研究你的代码,我不得不说它是如此的 Pythonic,以至于它让我大吃一惊。所以有人说,授人以鱼,你养他一天,教他钓鱼,你养他一辈子。我正在阅读请求文档,并了解一些基本的 html。我从你那里收到了一条鱼,除非你想让我一直回来请求你在网络抓取方面的帮助(你也会获得一些正义的分数),你能不能像你一样回顾一下你的想法和工作流程创建此代码?
    • @OmiSlash,我添加了一些更多的 cmets 来进一步分解它,希望这会让它更容易理解。
    【解决方案2】:

    问题 1:这些是我想要的输出中的内容,但是我怎样才能让 python 以字符串格式而不是像上面的 HTML 格式给我呢?

    您可以通过以下几行简单的操作来获取文本:

    state_name=states.find('h1').text
    

    同样的方法也适用于每一行。

    问题2:如何循环request.get(url01 to url56)?

    可以将相同的代码块放入从 1 到 56 的循环中,如下所示:

    for i in range(1,57):
        res = requests.get('http://livingwage.mit.edu/states/'+str(i).zfill(2))
        ...rest of the code...
    

    zfill 将添加那些前导零。此外,最好将requests.get 包含在try-except 块中,这样即使url 错误,循环也会优雅地继续。

    【讨论】:

    • 感谢您的回答,它对我的​​帮助很大,因为它遵循了我的编码方式。但我接受了 Padraic Cunningham 的回答,因为它比我编码的方式更像 Pythonic(我很惭愧!!)。从 Padraic Cunningham 的回答中,我也学到了一些用于网络抓取的新技术。学习新技能始终是目标,对吧?
    猜你喜欢
    • 2022-09-23
    • 2013-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多