Selenium网络爬虫

使用Selenium进行网络爬虫:初学者的指南

现代社交媒体等网站依赖JavaScript。然而,传统的脚本在提取动态元素方面存在不足,因为您需要JavaScript在获取数据之前先渲染整个页面。Selenium因其处理异步加载或无限滚动等问题的能力而受到欢迎。

本文将解释为什么您应该选择Selenium进行网络爬虫,并如何增加成功请求的机会。您还将找到一个逐步教程,教您如何使用Python构建一个Selenium网络爬虫。

Python与Selenium的网络爬虫是什么?

Selenium是一个通过编程控制无头浏览器的网络爬虫库。它允许您打开网站,浏览网页,与嵌入在JavaScript中的元素进行交互,并将它们提取出来供进一步使用。

如今,网站被设计为在笔记本电脑、平板电脑或智能手机等设备上运行。这些网站通过JavaScript进行客户端渲染,以响应用户的鼠标点击和键盘输入等操作。然而,这对于网络爬虫来说也带来了坏消息 — — 您必须处理懒加载或浏览器指纹技术。

使用常规的HTML提取工具如Requests来爬取动态网站变得非常困难。相反,Selenium模拟人类行为,因此您成功提取数据的机会增加了。

为什么选择Selenium进行网络爬虫?

Selenium之所以成为流行的网络爬虫选择,有多个原因:

  • 支持JavaScript渲染:Selenium主要用于浏览器自动化,因此非常适合爬取依赖JavaScript的网站。它能够完全渲染目标网站并提取数据。
  • 跨浏览器支持:Selenium最好的一点是它可以模拟主流浏览器,如Chrome、Firefox和Microsoft Edge。
  • 支持多种编程语言:Selenium在编程语言方面也很灵活,您可以使用Python、Java、Ruby和C#等进行开发。
  • 模拟用户行为:通过Selenium,您可以模仿人类的行为与网页进行交互,包括点击按钮、填写表单、提交数据、滚动页面以及浏览网页。
  • 处理验证码:有些网站使用验证码来防止类似机器人的活动。Selenium可以处理这些测试,通过在浏览器中显示验证码,让您解决验证码或与第三方服务集成来自动化此过程。
  • 防止指纹检测:Selenium具有类似selenium-stealth这样的包,可以隐藏您的数字指纹。正确配置后,它可以防止登录问题或通过reCAPTCHA。
  • 大型社区:Selenium拥有活跃的社区,这意味着有许多资源、教程和插件可供使用,为您提供更好的体验。

尽管Selenium是强大的网络爬虫工具,在某些情况下可能并不是最高效的选择。对于简单的爬取任务或处理静态网站,其他库如BeautifulSoup或Requests可能是更好的选择。此外,您还可以选择使用其他无界面库,如Puppeteer,它使用更少的资源。然而,当处理复杂的动态网站时,Selenium的功能使其成为可靠的选择。

准备构建Selenium网络爬虫

1.选择项目想法和确定项目参数

构建Selenium网络爬虫的第一步是确定项目参数。

在使用Selenium进行网络爬虫时,有几种编程语言可供选择。但是决定使用哪种语言可能会有些困难,因此我们比较了适用于爬取任务的流行语言(*并非所有语言都受到Selenium的支持)。如果您仍然不能决定,可以选择Python — — 它是最容易使用的语言之一,并且适用于大多数项目需求。

您不需要额外的库来获取或解析数据,因为Selenium具有其自己的包和模块,涵盖了所有网络爬取阶段。例如,如果您需要清理数据,可以安装selenium.by模块。否则,Selenium可以很好地与其他强大的解析器(如Beautiful Soup)配合使用。

现在,让我们转向项目本身。您可以选择爬取像亚马逊这样的真实目标网站,或者在特意为爬取而创建的网站上练习您的技能。这样,您可以在一个安全的环境中探索不同的技术、语言和工具。在我们的网络爬取沙箱列表中,您可以找到一些建议的网站。

当您掌握了Selenium后,可能想要将您的技能投入实际应用。比如说,您想要获取最佳的航班优惠,您可以构建一个爬虫来每日收集价格并下载结果。如果您还没有任何使用案例,请查阅我们的指南中为初学者和高级用户提供的一系列创意想法。

2. 考虑网络抓取指南

虽然网络爬虫可以为许多目的服务,但在道德和法律层面上,有一些准则需要遵循。

首先,尊重网站的服务条款,不要在登录后进行数据爬取。这样做可能会导致您陷入法律纠纷。在我们的网络爬虫最佳实践文章中,您可以找到更多建议。

此外,如果您对网站可能带来的所有挑战不了解,网络爬虫可能会变得繁重。从验证码和IP地址封禁到网站结构变化,这些障碍可能会干扰您的Selenium网络爬虫。

3. 使用代理服务器

为了爬取网站,您将需要多个IP地址。因此,考虑在Selenium中设置代理。这样,当您向目标网站发送连接请求时,每次都会以新访客的身份出现。

您很可能会被诱惑使用免费的代理列表,但如果您不希望有人滥用您的个人信息,请选择付费的服务提供商。一个小建议:您应该检查提供商是否为Selenium代理设置提供了广泛的技术文档。

有几种类型的代理可供选择,但我们建议使用住宅地址代理。这些代理来自真实用户的设备,它们不可避免地会轮换,而且大多数服务提供长时间的粘性会话。

如何使用 Selenium 进行网络抓取?分步教程

在本教程中,我们将逐步从 quotes.toscrape.com 抓取两个 URL:

这两个链接都包含由JavaScript生成的内容,而第二个页面会延迟渲染。为什么需要学习如何处理延迟加载的内容呢?有时候,页面加载需要一段时间,或者在提取数据之前需要等待特定元素(或条件)满足。

有些网页加载需要一段时间,或者在提取数据之前需要等待特定元素(或条件)满足。因此,您需要学习如何处理延迟加载的内容。

必备条件

  • Python 3:请确保您的系统已安装最新的 Python。您可以从 Python.org 官方网站下载。
  • Selenium:使用 pip 安装 Selenium 软件包。打开命令提示符或终端,运行以下命令: pip install selenium。
  • Chrome WebDriver: 下载与 Chrome 浏览器对应的 Chrome WebDriver

导入程序库

步骤1:按照以下指示编写您的第一个Selenium脚本。

备注:我们将使用Python和Chrome浏览器。因此,您需要将Chrome WebDriver添加到系统路径中,以便浏览器可以与Selenium正常工作。

步骤2:接着,安装Selenium。

1) 首先,从 Selenium 模块导入 Webdriver。

from selenium import webdriver

2) 然后使用 Selenium 的 By selector 模块导入网络驱动程序,以简化元素选择。

from selenium.webdriver.common.by import By

3) 在进入下一步之前,确保具备暂停爬虫的所有要素。

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions

4) 由于我们将使用 CSV 模块,因此也需要导入该模块。

import csv

设置全局变量和查找所需元素

步骤1. 现在,让我们设置全局变量,以便可以存储这些值:

  • url — 你将要爬取的页面链接;
  • timeout — 如果页面加载时间超过了设定的timeout,你的爬取将失败。所以,在解决错误之前,你需要指定一个等待时间,以等待元素出现;
  • output — 这是一个列表,你将在其中写入爬取到的引用语句。
url = 'http://quotes.toscrape.com/js/'
#url = 'http://quotes.toscrape.com/js-delayed/'
timeout = 10 #seconds
output = []

步骤 2. 然后,右键单击页面上的任意位置,检查 quotes.toscrape.com/js 的页面源。

步骤3.你需要选择所有的引用类对象,并在其中找到以下类别的文本内容:引用语句(quote)、作者(author)和标签(tag)。你可以使用Selenium的By模块来找到所有这些元素。

quotes = driver.find_elements(By.CLASS_NAME, 'quote')

步骤4.对于引用语句和作者,使用find_element()函数通过它们的类名来找到这些元素。然后提取文本并将其保存在一个变量中。

for quote in quotes:
    text = quote.find_element(By.CLASS_NAME, 'text').text
    print (f'Text: {text}')
    author = quote.find_element(By.CLASS_NAME, 'author').text
    print (f'Author: {author}')

步骤5.由于每个引用语句可能有多个标签,你需要使用find_elements()函数来找到所有这些标签。然后,遍历每个标签,并将其文本追加到标签列表(tags list)中。

tags = []
    for tag in quote.find_elements(By.CLASS_NAME, 'tag'):
        tags.append(tag.text)
    print (tags)

最后,你可以将这些变量放入一个字典中,然后将该字典追加到你创建的输出列表(output list)中。

output.append({
        'author': author,
        'text': text,
        'tags': tags,
    })

使用 Python Selenium 抓取动态网页

步骤1.首先,你需要使用 Selenium 设置一个浏览器。在本例中,我们将使用 Chromium。

def prepare_browser():
    #Initializing Chrome options
    chrome_options = webdriver.ChromeOptions()
    driver = webdriver.Chrome(options= chrome_options)
    return driver

备注:如果你需要添加selenium_stealth来掩盖你的数字指纹,或者设置代理来避免速率限制,那么这就是可以这么做的地方。如果你不知道如何在Python中使用Selenium设置代理,可以参考我们的指南,其中会详细解释一切。

步骤2.接下来,编写main()函数。通过调用prepare_browser()函数创建一个新的Webdriver,并将它与URL(http://quotes.toscrape.com/js/)一起传递给一个新的scrape()函数。在函数结束后,驱动程序将退出,然后你可以打印出整个输出结果。

def main():
    driver = prepare_browser()
    scrape(url, driver)
    driver.quit()
    print (output)
if __name__ == '__main__':
    main()

步骤3.现在,让我们开始爬取数据。通过driver.get(url)函数,我们告诉Selenium打开浏览器并访问该URL。由于选择器已经准备好了,只需粘贴代码即可。

def scrape(url, driver):
    driver.get(url)
    quotes = driver.find_elements(By.CLASS_NAME, 'quote')
    for quote in quotes:
        text = quote.find_element(By.CLASS_NAME, 'text').text
        print (f'Text: {text}')
        author = quote.find_element(By.CLASS_NAME, 'author').text
        print (f'Author: {author}')
        tags = []
        for tag in quote.find_elements(By.CLASS_NAME, 'tag'):
            tags.append(tag.text)
        print (tags)
        output.append({
            'author': author,
            'text': text,
            'tags': tags,
        })

这将会打开浏览器窗口,爬取一个页面,并将爬取到的文本打印到控制台,待脚本运行结束后,将输出整个结果。

控制台输出示例:

Opened: http://quotes.toscrape.com/js/
Text: “The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”
Author: Albert Einstein
['change', 'deep-thoughts', 'thinking', 'world']
Text: “It is our choices, Harry, that show what we truly are, far more than our abilities.”
Author: J.K. Rowling
['abilities', 'choices']
Text: “There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a 
miracle.”
Author: Albert Einstein
['inspirational', 'life', 'live', 'miracle', 'miracles']
Text: “The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”
Author: Jane Austen
['aliteracy', 'books', 'classic', 'humor']
Text: “Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”
Author: Marilyn Monroe
['be-yourself', 'inspirational']
Text: “Try not to become a man of success. Rather become a man of value.”
Author: Albert Einstein
['adulthood', 'success', 'value']

列表输出示例:

[{'author': 'Albert Einstein', 'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”', 'tags': ['change', 'deep-thoughts', 'thinking', 'world']}, {'author': 'J.K. Rowling', 'text': '“It is our choices, Harry, that show what we truly are, far more than our abilities.”', 'tags': ['abilities', 'choices']}, {'author': 'Albert Einstein', 'text': '“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”', 'tags': ['inspirational', 'life', 'live', 'miracle', 'miracles']}, {'author': 'Jane Austen', 'text': '“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”', 'tags': ['aliteracy', 'books', 'classic', 'humor']}, {'author': 'Marilyn Monroe', 'text': "“Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”", 'tags': ['be-yourself', 'inspirational']}, {'author': 'Albert Einstein', 'text': '“Try not to become a man of success. Rather become a man of value.”', 'tags': ['adulthood', 'success', 'value']}

抓取多个页面

步骤1.首先,找到带你前往下一页的链接。检查HTML代码;该链接将位于一个带有’class’属性为’next’的列表项下面。在该元素内部寻找<a>标签。

然后,在第二行中,从该元素中找到href属性,以获取下一页的URL,并将其赋值给next_url变量。

最后,再次调用scrape()函数,并将新的URL与Webdriver一起传递给它进行爬取。

elem_next = driver.find_element(By.CLASS_NAME, 'next').find_element(By.TAG_NAME,'a')
next_url = elem_next.get_attribute("href")
scrape(next_url, driver)

步骤2.现在脚本已经能够处理分页并爬取整个类别的数据了。然而,它仍然不知道何时停止,在最后一页上会发生错误,因为没有带有’class’属性为’next’的元素。

你可以将代码放在try except块中,以防止崩溃,并在到达最后一页后执行其他操作(如果需要的话)。

<try:
    elem_next = driver.find_element(By.CLASS_NAME, 'next').find_element(By.TAG_NAME,'a')
    next_url = elem_next.get_attribute("href")
    scrape(next_url, driver)
except:
    print('Next button not found. Quitting.')

等待页面加载

有时候,元素加载或由JavaScript生成可能需要一些时间。在这种情况下,你不希望在元素加载完成之前开始解析输出。例如,http://quotes.toscrape.com/js-delayed/ 页面会延迟10秒。如果你要搜索的元素尚未加载完成,脚本将会失败。

为了解决这个问题,你可以告诉Webdriver等待直到元素出现。最简单的方法是使用Selenium的WebDriverWait类。

步骤1.首先,你需要传递Webdriver和最开始创建的timeout变量,然后等待页面上带有’class’属性为’quote’的可见元素出现。然后你可以开始解析。

你还可以使用更多的条件进行等待。

WebDriverWait(driver, timeout).until(
     expected_conditions.presence_of_element_located((By.CLASS_NAME, 'quote'))
        )

步骤2.你也可以将上面的代码放在try except块中。这样,如果代码超时或页面上没有引用类的元素出现,你可以重试相同的请求。

现在,你的爬取函数已经完成了:

def scrape(url, driver):
    driver.get(url)
    print (f"Opened: {driver.current_url}")
    try:
        WebDriverWait(driver, timeout).until(
            expected_conditions.presence_of_element_located((By.CLASS_NAME, 'quote'))
        )
        # Finding all elements with a class of 'quote' in the page
        quotes = driver.find_elements(By.CLASS_NAME, 'quote')
        for quote in quotes:
            text = quote.find_element(By.CLASS_NAME, 'text').text
            print (f'Text: {text}')
            author = quote.find_element(By.CLASS_NAME, 'author').text
            print (f'Author: {author}')
            tags = []
            for tag in quote.find_elements(By.CLASS_NAME, 'tag'):
                tags.append(tag.text)
            print (tags)
            output.append({
                'author': author,
                'text': text,
                'tags': tags,
            })
        try:
            elem_next = driver.find_element(By.CLASS_NAME, 'next').find_element(By.TAG_NAME,'a')
            next_url = elem_next.get_attribute("href")
            scrape(next_url, driver)
        except:
            print('Next button not found. Quitting.')
    except:
        print ('Timed out.')

将输出保存到CSV文件中

最后,您可以在 main() 函数中添加几行,将输出写入 CSV 文件(使用 csv 库)。为此,我们需要一个名为 output_filename 的新变量。

field_names = ['author', 'text', 'tags']
    output_filename = 'quotes.csv'
    with open (output_filename, 'w', newline='', encoding='utf-8') as f_out:
        writer = csv.DictWriter(f_out, fieldnames = field_names)
        writer.writeheader()
        writer.writerows(output)

上面的代码会创建一个文件,并将field_names列表作为CSV文件的表头写入。然后,它会使用输出列表中的字典对象来填充文件。

这是完整的脚本:

from selenium import webdriver
# Using By to simplify selection
from selenium.webdriver.common.by import By
# The latter two will be used to make sure that needed elements are present 
# Before we begin scraping
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
# For writing output to a CSV file
import csv
url = 'http://quotes.toscrape.com/js/'
#url = 'http://quotes.toscrape.com/js-delayed/'
timeout = 20 #secondsoutput = []def prepare_browser() -> webdriver:
    # Initializing Chrome options
    chrome_options = webdriver.ChromeOptions()
    driver = webdriver.Chrome(options= chrome_options)
    return driverdef scrape(url: str, driver: webdriver) -> None:
    driver.get(url)
    print (f"Opened: {driver.current_url}")
    try:
        WebDriverWait(driver, timeout).until(
            expected_conditions.presence_of_element_located((By.CLASS_NAME, 'quote'))
        )
        # Finding all elements with a class of 'quote' in the page
        quotes = driver.find_elements(By.CLASS_NAME, 'quote')
        for quote in quotes:
            text = quote.find_element(By.CLASS_NAME, 'text').text
            print (f'Text: {text}')
            author = quote.find_element(By.CLASS_NAME, 'author').text
            print (f'Author: {author}')
            tags = []
            for tag in quote.find_elements(By.CLASS_NAME, 'tag'):
                tags.append(tag.text)
            print (tags)
            output.append({
                'author': author,
                'text': text,
                'tags': tags,
            })
        try:
            elem_next = driver.find_element(By.CLASS_NAME, 'next').find_element(By.TAG_NAME,'a')
            next_url = elem_next.get_attribute("href")
            scrape(next_url, driver)
        except:
            print('Next button not found. Quitting.')
    except:
        print ('Timed out.')def main() -> None:
    driver = prepare_browser()
    scrape(url, driver)
    driver.quit()
    print (output)
    # CSV
    field_names = ['author', 'text', 'tags']
    output_filename = 'quotes.csv'
    with open (output_filename, 'w', newline='', encoding='utf-8') as f_out:
        writer = csv.DictWriter(f_out, fieldnames = field_names)
        writer.writeheader()
        writer.writerows(output)if __name__ == '__main__':
    main()

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *