如何通过多线程代码python提高Webscraping代码速度

Posted

技术标签:

【中文标题】如何通过多线程代码python提高Webscraping代码速度【英文标题】:how to improve the Webscraping code speed by multi threading code python 【发布时间】:2020-12-29 15:32:10 【问题描述】:

下面是我逐行编写的代码(大约有 900 页,每行有 10 行和 5 个数据) 有什么方法可以加快速度。目前将数据导出到 csv 需要 80 分钟。有什么方法可以对页面进行并行请求并提高此代码的效率。

import requests
from urllib3.exceptions import InsecureRequestWarning
import csv

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from bs4 import BeautifulSoup as bs

f = csv.writer(open('GEM.csv', 'w', newline=''))
f.writerow(['Bidnumber', 'Items', 'Quantitiy', 'Department', 'Enddate'])


def scrap_bid_data():
    page_no = 1
    while page_no < 910:
        print('Hold on creating URL to fetch data...')
        url = 'https://bidplus.gem.gov.in/bidlists?bidlists&page_no=' + str(page_no)
        print('URL created: ' + url)
        scraped_data = requests.get(url, verify=False)
        soup_data = bs(scraped_data.text, 'lxml')
        extracted_data = soup_data.find('div', 'id': 'pagi_content')
        if len(extracted_data) == 0:
            break
        else:
            for idx in range(len(extracted_data)):
                if (idx % 2 == 1):
                    bid_data = extracted_data.contents[idx].text.strip().split('\n')

                    bidno = bid_data[0].split(":")[-1]
                    items = bid_data[5].split(":")[-1]
                    qnty = int(bid_data[6].split(':')[1].strip())
                    dept = (bid_data[10] + bid_data[12].strip()).split(":")[-1]
                    edate = bid_data[17].split("End Date:")[-1]
                    f.writerow([bidno, items, qnty, dept, edate])

            page_no=page_no+1
scrap_bid_data()

【问题讨论】:

尝试使用 pandas 和字典 ***.com/questions/57000903/… 此代码受 IO 限制 - 大多数时间将用于发出 HTTP 请求 - 因此优化它的方法是使用线程或类似方法并行化 HTTP 请求。 @snakecharmerb 抱歉不知道 THread 你能帮忙吗! 作为python线程的简单介绍,我推荐这篇文章:medium.com/better-programming/… 【参考方案1】:

我对您的代码进行了一些重组,以确保您的 CSV 文件已关闭。我还收到以下错误消息:

ConnectionError: HTTPSConnectionPool(host='bidplus.gem.gov.in', port=443): url: /bidlists?bidlists&page_no=1 超过最大重试次数(由 NewConnectionError(':建立新连接失败:[WinError 10060]连接尝试失败,因为连接方一段时间后没有正确响应,或者连接失败,因为连接的主机没有响应',))

您应该尝试NUMBER_THREADS 值:

import requests
from urllib3.exceptions import InsecureRequestWarning
import csv
import concurrent.futures
import functools

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from bs4 import BeautifulSoup as bs


def download_page(session, page_no):
    url = 'https://bidplus.gem.gov.in/bidlists?bidlists&page_no=' + str(page_no)
    print('URL created: ' + url)
    resp = session.get(url, verify=False)
    return resp.text


def scrap_bid_data():
    NUMBER_THREADS = 30 # number of concurrent download requests
    with open('GEM.csv', 'w', newline='') as out_file:
        f = csv.writer(out_file)
        f.writerow(['Bidnumber', 'Items', 'Quantitiy', 'Department', 'Enddate'])
        with requests.Session() as session:
            page_downloader = functools.partial(download_page, session)
            with concurrent.futures.ThreadPoolExecutor(max_workers=NUMBER_THREADS) as executor:
                pages = executor.map(page_downloader, range(1, 910))
                page_no = 0
                for page in pages:
                    page_no += 1
                    soup_data = bs(page, 'lxml')
                    extracted_data = soup_data.find('div', 'id': 'pagi_content')
                    if extracted_data is None or len(extracted_data) == 0:
                        print('No data at page number', page_no)
                        print(page)
                        break
                    else:
                        for idx in range(len(extracted_data)):
                            if (idx % 2 == 1):
                                bid_data = extracted_data.contents[idx].text.strip().split('\n')

                                bidno = bid_data[0].split(":")[-1]
                                items = bid_data[5].split(":")[-1]
                                qnty = int(bid_data[6].split(':')[1].strip())
                                dept = (bid_data[10] + bid_data[12].strip()).split(":")[-1]
                                edate = bid_data[17].split("End Date:")[-1]
                                f.writerow([bidno, items, qnty, dept, edate])
scrap_bid_data()

【讨论】:

线程 30 在 excel @Booboo 中写入了 8770 条记录中的 700 条记录 那有什么意义? 有例外吗?我什至不能返回一个 URL?你有if len(extracted_data) == 0: break。我将修改代码以跟踪执行时实际处理的页面数。 我已更新代码以明确计算处理的页面数。我相信您的原始代码只检索第 1 页到第 909 页,所以这就是我的工作。 为什么我会收到“无法访问此站点”。浏览器错误和requests 超时错误,而您没有?

以上是关于如何通过多线程代码python提高Webscraping代码速度的主要内容,如果未能解决你的问题,请参考以下文章

Python通过多线程实现 `异步`

如何通过多线程使用socket和pyqt避免数据丢失

如何通过多线程概念下载单个文件

python通过多线程并获取返回值

如何通过多线程 Java 编程最大限度地利用资源(RAM 和 CPU)?

基于SmartThreadPool线程池技术实现多任务批量处理