Skip to main content
 首页 » 编程设计

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

2024年08月28日31freeliver54

我带着我的第一个真正的 python 程序的错误来到这里(即不是 codeacademy 的东西)。我是 R 的狂热用户,我使用 XML 包构建了一堆网络爬虫/抓取工具。不幸的是,我发现 R 并不适合我尝试做的某些事情,因此我正在用 Python 构建其中一些工具,希望您能提供帮助。如果有任何我忽略的明显的特定于 Python 的编码最佳实践,我也将不胜感激。

我需要能够将值附加到我的可迭代对象中。在下面代码的每一步迭代中,整个对象 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 

请您参考如下方法:

您在 links 上循环,并且也在其上追加。最终它会命中您添加的第一个 links_child,这是一个链接列表,而不是 Tag 对象,因此没有 get 属性。

将 links_child 附加到另一个变量,它工作正常。您还可以使用 extend 而不是 append 将 links_child 的内容添加到 links 但它进一步遇到了另一个问题在尝试读取您没有考虑的相对 URL ../contact/contact-form.php 时。

有几种方法可以在 Python 中进行多处理,最流行的是 multiprocessing因为它为您提供了一个很好的 API,可以与生成进程一起使用,而不是可以充分利用 CPU 中的多个内核的线程。

在此示例中,您可以通过多种方式处理多处理。例如,您可以将主循环定义为一个函数 add create a Pool of workers to work through it。像这样:

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 

尽管以此为指导,我简化了您的函数以使其更清晰。希望这会让您走上正轨。