如何处理大规模的网页抓取?

How to handle large scale Web Scraping?

情况:

我最近开始使用 selenium 和 scrapy 进行网络抓取,我正在做一个项目,我有一个包含 42,000 个邮政编码的 csv 文件,我的工作是获取该邮政编码并继续 site 输入邮政编码并抓取所有结果。

问题:

这里的问题是,在执行此操作时,我必须连续单击 'load more' 按钮,直到显示所有结果,只有在完成后我才能收集数据。

这可能不是什么大问题,但是每个邮政编码需要 2 分钟,而我有 42 000 个可以使用。

代码:

    import scrapy
    from numpy.lib.npyio import load
    from selenium import webdriver
    from selenium.common.exceptions import ElementClickInterceptedException, ElementNotInteractableException, ElementNotSelectableException, NoSuchElementException, StaleElementReferenceException
    from selenium.webdriver.common.keys import Keys
    from items import CareCreditItem
    from datetime import datetime
    import os
    
    
    from scrapy.crawler import CrawlerProcess
    global pin_code
    pin_code = input("enter pin code")
    
    class CareCredit1Spider(scrapy.Spider):
        
        name = 'care_credit_1'
        start_urls = ['https://www.carecredit.com/doctor-locator/results/Any-Profession/Any-Specialty//?Sort=D&Radius=75&Page=1']
    
        def start_requests(self):
            
            directory = os.getcwd()
            options = webdriver.ChromeOptions()
            options.headless = True
    
            options.add_experimental_option("excludeSwitches", ["enable-logging"])
            path = (directory+r"\Chromedriver.exe")
            driver = webdriver.Chrome(path,options=options)
    
            #URL of the website
            url = "https://www.carecredit.com/doctor-locator/results/Any-Profession/Any-Specialty/" +pin_code + "/?Sort=D&Radius=75&Page=1"
            driver.maximize_window()
            #opening link in the browser
            driver.get(url)
            driver.implicitly_wait(200)
            
            try:
                cookies = driver.find_element_by_xpath('//*[@id="onetrust-accept-btn-handler"]')
                cookies.click()
            except:
                pass
    
            i = 0
            loadMoreButtonExists = True
            while loadMoreButtonExists:
                try:
                    load_more =  driver.find_element_by_xpath('//*[@id="next-page"]')
                    load_more.click()    
                    driver.implicitly_wait(30)
                except ElementNotInteractableException:
                    loadMoreButtonExists = False
                except ElementClickInterceptedException:
                    pass
                except StaleElementReferenceException:
                    pass
                except NoSuchElementException:
                    loadMoreButtonExists = False
    
            try:
                previous_page = driver.find_element_by_xpath('//*[@id="previous-page"]')
                previous_page.click()
            except:
                pass
    
            name = driver.find_elements_by_class_name('dl-result-item')
            r = 1
            temp_list=[]
            j = 0
            for element in name:
                link = element.find_element_by_tag_name('a')
                c = link.get_property('href')
                yield scrapy.Request(c)
    
        def parse(self, response):
            item = CareCreditItem()
            item['Practise_name'] = response.css('h1 ::text').get()
            item['address'] = response.css('.google-maps-external ::text').get()
            item['phone_no'] = response.css('.dl-detail-phone ::text').get()
            yield item
    now = datetime.now()
    dt_string = now.strftime("%d/%m/%Y")
    dt = now.strftime("%H-%M-%S")
    file_name = dt_string+"_"+dt+"zip-code"+pin_code+".csv"
    process = CrawlerProcess(settings={
        'FEED_URI' : file_name,
        'FEED_FORMAT':'csv'
    })
    process.crawl(CareCredit1Spider)
    process.start()
    print("CSV File is Ready")

items.py


    import scrapy

    class CareCreditItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        Practise_name = scrapy.Field()
        address = scrapy.Field()
        phone_no = scrapy.Field()

问题:

基本上我的问题很简单。有没有办法优化此代码以使其执行得更快?或者还有哪些其他可能的方法可以在不花很长时间的情况下处理这些数据?

由于站点从 api 动态加载数据,您可以直接从 api 检索数据。这会加快速度,但我仍然会等待以避免达到速率限制。

import requests
import time
import pandas as pd

zipcode = '00704'
radius = 75
url = f'https://www.carecredit.com/sites/ContentServer?d=&pagename=CCGetLocatorService&Zip={zipcode}&City=&State=&Lat=&Long=&Sort=D&Radius={radius}&PracticePhone=&Profession=&location={zipcode}&Page=1'
req = requests.get(url)
r = req.json()
data = r['results']

for i in range(2,r['maxPage']+1):
    url = f'https://www.carecredit.com/sites/ContentServer?d=&pagename=CCGetLocatorService&Zip={zipcode}&City=&State=&Lat=&Long=&Sort=D&Radius={radius}&PracticePhone=&Profession=&location={zipcode}&Page={i}'
    req = requests.get(url)
    r = req.json()
    data.extend(r['results'])
    time.sleep(1)

df = pd.DataFrame(data)
df.to_csv(f'{pd.Timestamp.now().strftime("%d/%m/%Y_%H-%M-%S")}zip-code{zipcode}.csv')

您可以通过多种方式执行此操作。

1.创建一个分布式系统,其中您 运行 蜘蛛通过多台机器以便 运行 并行。

我认为这是更好的选择,因为您还可以创建一个可扩展的动态解决方案,您将能够多次使用它。

通常有很多方法可以做到这一点,包括将种子列表(邮政编码)分成许多单独的种子列表,以便让单独的进程使用单独的种子列表,因此下载将 运行并行所以例如如果它在 2 台机器上它会快 2 倍,但如果在 10 台机器上它会快 10 倍,等等

为了做到这一点,我可能建议研究一下 AWS,即 AWS Lambda , AWS EC2 Instances or even AWS Spot Instances 这些是我以前使用过的,使用起来并不难。

2。或者,如果您想 运行 在一台机器上执行它,您可以查看 Multithreading with Python,它可以帮助您 运行 在单台机器上并行执行该过程。

3。这是另一种选择,特别是如果它是一次性过程。您可以尝试 运行ning 简单地使用可能会加快速度的请求,但是如果有大量的种子,并行开发 运行ning 过程通常会更快。