【问题标题】:How do I make my web scraping script more robust?如何使我的网页抓取脚本更健壮?
【发布时间】:2019-10-25 14:30:43
【问题描述】:

我启动了一个代码来抓取 Santander 网站。

抓取似乎有效,除了我得到错误的结果。而当我连续运行两次代码时,结果会发生变化。

如何让抓取更健壮,问题是当我运行代码并一一检查结果时,它似乎运行良好。

def hw_santander_scrape(Amount, Duration):
  from selenium import webdriver
  from selenium.webdriver.support.ui import WebDriverWait
  from selenium.webdriver.support import expected_conditions as EC
  chrome_options = webdriver.ChromeOptions()
  chrome_options.add_argument('--headless')
  chrome_options.add_argument('--no-sandbox')
  chrome_options.add_argument('--disable-dev-shm-usage')
  chrome_options.add_argument('--start-maximized')
  chrome_options.add_argument('window-size=10000x5000')
  webdriver = webdriver.Chrome('chromedriver', chrome_options = chrome_options)

  #
  import time
  maintenant = DT.now()
  period = str(maintenant.day) + '_' + str(maintenant.month) + '_' + str(maintenant.year)
  print('Start Scraping')

  ################################################ Santander###############################################

  Santander = pd.DataFrame({
      'Project': "reforma vivienda",
      'Period': period,
      'Monthly repayment': [0],
      'TIN': [0],
      'TAE': [0],
      'Total repayment': [0],
      'Initial amount': [0],
      'Duration': [0]
  })

  project = pd.DataFrame({
      'Project': "reforma vivienda",
      'Period': period,
      'Monthly repayment': [0],
      'TIN': [0],
      'TAE': [0],
      'Total repayment': [0],
      'Initial amount': [0],
      'Duration': [0]
  })
  url = 'https://simuladores.bancosantander.es/SantanderES/loansimulatorweb.aspx?por=webpublica&prv=publico&m=300&cta=1&ls=0#/t0'
  webdriver.get(url)

  Max_amount = 90.000
  Min_amount = 3.000
  for i in range(len(Amount)):
    Simulated_amount = Amount[i]
    if Simulated_amount > Max_amount:
      pass
    elif Simulated_amount < Min_amount:
      pass
    else :
      amount = WebDriverWait(webdriver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "#amount")))
      amount.clear()
      amount.send_keys("{:.3f}".format(Simulated_amount))
      WebDriverWait(webdriver, 30).until(lambda webdriver: webdriver.execute_script('return jQuery.active') == 0)
      for j in range(len(Duration)):
        Simulated_duration = Duration[j]
        Simulated_duration = round(int(Simulated_duration))
        Max_duration = 96
        Min_duration = 12
        if Simulated_duration > Max_duration:
          pass
        elif Simulated_duration < Min_duration:
          pass
        else :
          term = WebDriverWait(webdriver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "#term")))
        term.clear()
        term.send_keys("{}".format(Simulated_duration))
        term.send_keys(Keys.TAB)
        webdriver.save_screenshot('screenshot_santander.png')
        project.loc[j, 'Project'] = "reforma vivienda"
        project.loc[j, 'Initial amount'] = float("{:.3f}".format(Amount[i]).replace('.', ''))
        project.loc[j, 'Duration'] = Simulated_duration
        project.loc[j, 'Period'] = str(maintenant.day) + '/' + str(maintenant.month) + '/' + str(maintenant.year)
        project.loc[j, 'Monthly repayment'] = webdriver.find_element_by_css_selector('.r1 span').text.replace(' €', '').replace(',', '.')
        project.loc[j, 'TIN'] = float(webdriver.find_element_by_css_selector('.r3 span').text[6: 10].replace(',', '.'))
        project.loc[j, 'TAE'] = float(webdriver.find_element_by_css_selector('.r3 span').text[13: 17].replace(',', '.'))
        project.loc[j, 'Total repayment'] = float(webdriver.find_element_by_css_selector('.r7 span').text.replace(' €', '').replace('.', '').replace(',', '.'))
      Santander = Santander.append(project)
  Santander = Santander.loc[Santander.TIN != 0,: ]
  Santander.to_csv('Santander_{}.csv'.format(period), index = False)
print('End Scraping')

运行代码:

Amount = [13.000, 14.000, 15.000, 30.000, 45.000, 60.000]
Duration = [12, 15, 24, 36, 48, 60, 72, 84, 96]
hw_santander_scrape(Amount, Duration)

【问题讨论】:

    标签: python selenium-webdriver web-scraping beautifulsoup selenium-chromedriver


    【解决方案1】:

    这是我大放异彩的时候了!

    信息:

    我目前正在开发一个面临同样问题的金融数据聚合器。

    它从大约十几个网站收集数据并将其组织成一个 JSON 对象,然后由 Flask 站点使用来显示数据。

    这些数据是从具有多个子目录的网站中抓取的,这些子目录具有相似的内容,具有不同的选择器。

    您可以想象,使用像 selenium 这样的框架会变得非常复杂,因此唯一的解决方案是将其简化。

    解决办法:

    Simplicity is key,所以我删除了除BeautifulSouprequests 库之外的所有依赖项。

    然后我为每个filter创建了三个类和一个函数[1]

    from bs4 import BeautifulSoup
    
    class GET:
      def text(soup, selector, index = 0):
        selected = soup.select(selector)
        if len(selected) > index:
          return selected[index].text.strip()
    
    class Parse:
      def common(soup, selector):
        return GET.text(soup, selector, index = 5)
    
    class Routes:
      def main(self):
        data = {}
        if self.is_dir_1:
          data["name"] = GET.text(self.soup, "div")
          data["title-data"] = Parse.common(self.soup, "p > div:nth-child(1)")
        elif self.is_dir_2:
          data["name"] = GET.text(self.soup, "p", index = 2)
          data["title-data"] = Parse.common(self.soup, "p > div:nth-child(5)")
        return data
    
    def filter_name(url: str, response: str, filter_type: str):
      if hasattr(Routes, filter_type):
        return getattr(Routes, filter_type)(to_object({
          "is_dir_1": bool("/sub_dir_1/" in url),
          "is_dir_2": bool("/sub_dir_1/" in url),
          "soup": BeautifulSoup(html, "lxml")
        }))
      return {}
    

    使用requests 库我发出了获取数据的请求,然后我将URL、响应文本和filter_type 传递给filter_name 函数。

    然后在filter_name 函数中,我使用filter_type 参数将"soup" 传递给目标路由函数并选择每个元素并在那里获取它的数据。

    然后在目标路由函数中,我使用if条件来确定子目录并将文本分配给数据对象。

    这一切都完成后,我返回了data 对象。

    这种方法非常简单,并且保持我的代码 DRY,它甚至允许可选的 key: value 对。

    这里是 to_object 助手类的代码:

    class to_object(object):
      def __init__(self, dictionary):
        self.__dict__ = dictionary
    

    这会将字典转换为对象,因此不必总是写:

    self["soup"]
    

    你会写:

    self.soup
    

    修复错误:

    您确实需要标准化您使用的缩进类型,因为您的脚本会引发以下错误:

    Traceback (most recent call last):
      File "", line 84
        Amount =   [13.000, 14.000, 15.000, 30.000, 45.000, 60.000]
        ^
    IndentationError: unindent does not match any outer indentation level
    

    注意事项:

    1. 过滤器是抓取不同站点的脚本,我的项目要求我抓取多个站点以获取所需的数据。
    2. 多尝试整理你的代码,整理的代码更易读更写

    我希望这会有所帮助,祝你好运。

    【讨论】:

    • 其实你的回答并没有解决我的顾虑。我希望我的代码是健壮的,并且我收集的金额和利率与网站上的内容相对应。我正在尝试改进我当前的代码。
    • 这不是你可以用给定的代码解决的问题,你能详细解释一下 "robust" 是什么意思吗?
    • 事实上,当我运行代码一次时,我得到了正确的利率,而第二次我得到了不同的利率。而且我收回的金额是错误的。
    • 会不会是因为利率变了?另外,您是如何计算要收回的金额的?是数学问题还是您使用了错误的选择器?
    • 这就是选择器的问题。
    【解决方案2】:

    该数据来自 XHR。因此,只需使用请求来发布您的值并使用 json.loads 解析响应

    使用您的浏览器网络选项卡查看请求的外观。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-23
      • 1970-01-01
      • 2014-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多