在python / BeautifulSoup中进行Web爬网时将值附加到可迭代对象(也想了解多线程)

时间:2014-01-10 15:47:03

标签: python multithreading web-crawler

我来到这里是因为我的第一个真正的python程序有一个错误(即没有出现在codeacademy之外的东西)。我是R的狂热用户,我使用XML包构建了一堆网络抓取/抓取工具。不幸的是,我已经达到了R对于我正在尝试做的一些事情并不理想的地步,所以我在Python中构建了一些这些工具,希望你能提供帮助。如果有任何明显的python特定的编码最佳实践,我忽略了,我也会欣赏一个单挑。

我需要能够将值附加到我的iterable中。在下面的代码中的每个迭代步骤中,整个对象links_child被添加到我的links对象中。实际上,我需要将links_child中的每个链接分别添加到对象中,而不是全部作为一个条目添加。这将继续增长和增长links,只有当我达到指定数量的网站(下面的代码中为100)时,迭代才会中断。你们中的任何人都知道如何逐行提取bs4项并将它们添加到我的可迭代对象中吗?我得到的错误如下:

AttributeError: 'ResultSet' object has no attribute 'get'

另外,我最终希望这个爬虫能够快得多。我和beautifulsoup有什么类型的多线程选项(如果有的话)?我应该切换库吗?我可以在这里选择任何可以提高速度的低调水果吗?如果有一个简单的方法让5-10个线程同时执行此爬行并更新相同的字典对象,那将是完美的,但这可能只是一个幻想。

from urllib import urlopen
from bs4 import BeautifulSoup
import re

base_site = "http://www.tasq.com"
page = urlopen(base_site).read()
soup = BeautifulSoup(page)
links = soup.find_all('a')
start_slash = re.compile('/')
link_db = {}
# iterate through all links on the homepage
for link in links:
    # print for debugging purposes
    print link
    # pull out the hrefs from the current link
    fullLink = str(link.get('href'))
    # print for debugging purposes
    print fullLink
    # see if the link is valid using regex
    check_start = start_slash.match(fullLink)
    # if the link is not valid, concatenate the domain
    if check_start <> None:
        fullLink = base_site + fullLink
    # fi the link is already stored as a key in the dict, skip it
    if fullLink in link_db:
        next
    # connect to the full link (O operation)
    page_child = urlopen(fullLink).read()
    # create bs4 object out of the opened page
    soup_child = BeautifulSoup(page_child)
    # insert the link as the key and some text string as the value
    link_db[fullLink] = 'example'
    # find all links on current page and save them in object
    links_child = soup_child.find_all('a')
    # (THIS IS THE SOURCE OF THE ERROR)append object with links to the iterable
    links.append(links_child)
    # break code if link_db gets to specified length
    if len(link_db) == 100:
        break

1 个答案:

答案 0 :(得分:3)

你正在links骑自行车而且你也会越过它。最终它会点击您添加的第一个links_child,这是一个链接列表,而不是Tag对象,因此没有get属性。

links_child附加到另一个变量,它可以正常工作。您也可以使用extend代替appendlinks_child的内容添加到links,但在尝试阅读相对网址../contact/contact-form.php时又会遇到另一个问题你没有考虑到。

在Python中有多种方法可以进行多处理,最受欢迎的是multiprocessing,因为它为您提供了一个很好的API以及产生进程,而不是完全利用CPU中多个核心的线程。

在此示例中,您可以通过多种方式处理多处理。例如,您可以将主循环定义为函数add,创建一个工作池来完成它。像这样:

from urllib import urlopen
from bs4 import BeautifulSoup
import re
import multiprocessing

def work(link):
    link_db = {}
    start_slash = re.compile('/')
    print link
    fullLink = link.attrs.get('href', None)
    check_start = start_slash.match(fullLink)
    if check_start != None:
        fullLink = base_site + fullLink
    page_child = urlopen(fullLink).read()
    soup_child = BeautifulSoup(page_child)
    link_db[fullLink] = 'example'
    return link_db

if __name__ == '__main__':
    base_site = "http://www.tasq.com"
    page = urlopen(base_site).read()
    soup = BeautifulSoup(page)
    links = soup.find_all('a')
    link_dbs = []
    pool = multiprocessing.Pool(processes=4)
    result = pool.map_async(work, links)
    link_dbs.extend( result.get() )
    print link_dbs

以此为准则,我简化了你的功能,使其更清晰。希望这会让你走上正轨。