使用 Requests-HTML 时如何减少抓取时间?

问题描述

我目前使用 Requests-HTML 0.10.0 版和 Selenium 3.141.0。我的项目是抓取本网站 https://openreview.net/group?id=ICLR.cc/2021/Conference 上所有文章的评分。要打开网站的每个页面(网站有 53 个页面,每个页面有 50 篇文章),我使用 Selenium。接下来,为了在每个页面上打开文章,我使用 Requests-HTML。我的问题是如何减少打开每篇文章并获得评分所用的时间。在本例中,我使用 await r_inside.html.arender(sleep = 5,timeout=100),这意味着睡眠时间为 5 秒,超时为 100 秒。当我尝试将睡眠时间减少到 0.5 秒时,它会导致错误,这是因为它没有足够的时间来抓取网站。但是,如果我保持睡眠时间为 5 秒,则需要 6 到 13 个小时才能抓取所有 2600 篇文章。此外,在等待 13 小时后,我可以抓取所有 2600 篇文章,但是代码使用了 88 GB 的 RAM,我不喜欢这种情况,因为我需要将此代码发送给没有足够 RAM 来运行的其他人。我的目的是减少抓取时间和 RAM 内存。下面是我使用的代码

import csv

link = 'https://openreview.net/group?id=ICLR.cc/2021/Conference'

from requests_html import HTMLSession,AsyncHTMLSession

import time
from tqdm import tqdm
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import webdriverwait
id_list = []
keyword_list = []
abstract_list = []
title_list = []
driver = webdriver.Chrome('./requests_html/chromedriver.exe')
driver.get('https://openreview.net/group?id=ICLR.cc/2021/Conference')

cond = EC.presence_of_element_located((By.XPATH,'//*[@id="all-submissions"]/nav/ul/li[13]/a'))
webdriverwait(driver,10).until(cond)


for page in tqdm(range(1,54)):
    text = ''
    elems = driver.find_elements_by_xpath('//*[@id="all-submissions"]/ul/li')
    for i,elem in enumerate(elems):
        try:
            # parse title
            title = elem.find_element_by_xpath('./h4/a[1]')
            link = title.get_attribute('href')
            paper_id = link.split('=')[-1]
            title = title.text.strip().replace('\t',' ').replace('\n',' ')
            # show details
            elem.find_element_by_xpath('./a').click()
            time.sleep(0.2)

            # parse keywords & abstract
            items = elem.find_elements_by_xpath('.//li')
            keyword = ''.join([x.text for x in items if 'Keywords' in x.text])
            abstract = ''.join([x.text for x in items if 'Abstract' in x.text])
            keyword = keyword.strip().replace('\t',' ').replace('Keywords: ','')
            abstract = abstract.strip().replace('\t',' ').replace('Abstract: ','')
            text += paper_id+'\t'+title+'\t'+link+'\t'+keyword+'\t'+abstract+'\n'
            title_list.append(title)
            id_list.append(paper_id)
            keyword_list.append(keyword)
            abstract_list.append(abstract)
        except Exception as e:
            print(f'page {page},# {i}:',e)
            continue



    # next page
    try:
        driver.find_element_by_xpath('//*[@id="all-submissions"]/nav/ul/li[13]/a').click()
        time.sleep(2) # NOTE: increase sleep time if needed
    except:
        print('no next page,exit.')
        break

csv_file = open('./requests_html/bb_website_scrap.csv','w',encoding="utf-8")
csv_writer = csv.writer(csv_file)
csv_writer.writerow(['Title','Keyword','Abstract','Link','Total Number of Reviews','Average rating','Average Confidence'])
n = 0
for item in range(len(id_list)):
     title = title_list[item]
     keyword = keyword_list[item]
     abstract = abstract_list[item]
     id = id_list[item]

     link_pdf = f'https://openreview.net/forum?id={id}'
     print(id)

     asession_inside = AsyncHTMLSession()
     r_inside = await asession_inside.get(link_pdf)
     print(type(r_inside))
     await r_inside.html.arender(sleep = 5,timeout=100)

     test_rating = r_inside.html.find('div.comment-level-odd div.note_contents span.note_content_value')
     print(len(test_rating))
     check_list = {'0','1','2','3','4','5','6','7','8','9','10'}
     total_rating_confidence = []
     total_rating = []
     total_confidence = []
     for t in range(len(test_rating)):
          if any(test_rating[t].text.split(':')[0] in s for s in check_list):
               total_rating_confidence.append(test_rating[t].text.split(':')[0])

     for r in range(len(total_rating_confidence)):
          if (r % 2 == 0):
               total_rating.append(int(total_rating_confidence[r]))
          else:
               total_confidence.append(int(total_rating_confidence[r]))

     average_rating = sum(total_rating) / len(total_rating)
     average_confidence = sum(total_confidence) / len(total_confidence)
     csv_writer.writerow([title,keyword,abstract,link_pdf,len(total_rating),average_rating,average_confidence])
     n = n + 1
     print('Order {}',n)
csv_file.close()

解决方法

我不是 Python 专家(实际上,我是初学者),但简单的答案是更好的并行性和会话管理。

有用的答案有点复杂。

您要离开 Chromium 会话了,这很可能会占用您所有的 RAM。如果您调用 asession_inside.close(),您可能会看到 RAM 使用情况有所改善。

据我所知,你做的一切都是连续的;您获取每个页面并连续提取有关文章的数据。然后,您也依次查询每篇文章。

您正在使用 arender 异步获取每篇文章,但您正在等待它并使用标准的 for 循环。据我了解,这意味着您没有从 async 中获得任何优势;您仍在一次处理每一页(这说明您的处理时间很长)。

我建议使用 asyncio 将 for 循环转换为建议的 in this article 自身的并行版本。确保您设置了任务限制,这样您就不会尝试一次加载所有文章;这也将有助于您使用 RAM。