python学习-网络爬虫

242 阅读15分钟

python学习-网络爬虫

本文介绍下 Python 中使用 Selenium 进行网络爬虫的基本操作,包括网络爬虫简介、Selenium 简介、环境准备、Selenium基础、页面元素操作、高级操作、处理Cookies、异常处理和数据提取与解析等。

供自己以后查漏补缺,也欢迎同道朋友交流学习。

引言

之前介绍的都是运行自己写的程序,但还可以使用网络爬虫获取别人网站上的信息,并对其进行处理。

本章主要介绍下 Python 中使用 Selenium 进行网络爬虫的基本操作,包括网络爬虫简介、Selenium 简介、环境准备、Selenium基础、页面元素操作、高级操作、处理Cookies、异常处理和数据提取与解析等。

网络爬虫简介

网络爬虫(Web Crawler),也称为网页蜘蛛(Spider)或爬虫(Crawler),是一种自动浏览网络的软件,用于从互联网上下载网页。它的主要任务是抓取网页内容,以便进行后续的数据处理和分析

网络爬虫可以用于多种目的,包括但不限于数据收集信息聚合搜索引擎构建等。

  • 工作原理网络爬虫从一个或多个种子 URL 开始,访问这些页面,提取页面中的链接,然后递归地访问这些链接指向的页面,如此循环,直到满足特定条件(如深度限制时间限制等)。
  • 关键技术:包括 URL 管理、内容下载、链接提取、内容解析数据存储等。

Selenium简介

Selenium 是一个开源的自动化测试工具,广泛用于 Web 应用程序的测试。它提供了一套工具和库,允许开发者模拟用户与浏览器的交互,实现自动化测试

Selenium的作用

  • 自动化测试Selenium 最初被设计用于自动化 Web 应用程序的测试,它可以模拟用户的各种操作,如点击输入滚动等。
  • 爬虫工具:由于能够执行 JS 并模拟真实用户行为,它也被用作一种爬虫工具,尤其是在需要处理 JS 渲染的页面时。
  • 浏览器自动化:可以自动化浏览器操作,用于网页截图表单填充文件上传等任务。

Selenium的特点

  • 跨浏览器支持:支持多种浏览器,包括 ChromeFirefoxInternet ExplorerEdge等。
  • 跨平台支持:可以在 WindowsLinuxMac OS 上运行。
  • 编程语言兼容性:提供了多种语言的绑定,如 JavaC#PythonRuby等。
  • 丰富的API:提供了丰富的 API,可以精确控制浏览器行为。
  • 集成WebDriver:通过 WebDriver 协议与浏览器交互,可以启动和停止浏览器,以及执行复杂的浏览器操作。

适用场景

  • 自动化测试:对于需要自动化测试 Web 应用程序的场景,Selenium 是理想的选择。
  • 动态内容爬取:对于那些通过 JS 动态加载内容的网站,可以模拟用户行为,获取动态生成的数据。
  • 用户行为模拟:需要模拟用户登录、表单提交等复杂交互的场景。
  • 浏览器兼容性测试:需要测试 Web 应用程序在不同浏览器和不同操作系统上的表现。

环境准备

在开始使用 Selenium 进行自动化测试或网络爬虫之前,需要做好环境的准备工作,包括安装 Selenium 库和配置 WebDriver

Selenium库的安装

pip install selenium

WebDriver的安装和配置

我们大部分人都用 chrome 浏览器,那就安装 ChromeDriver

  • 下载:访问 ChromeDriver 下载页面,选择与你的 Chrome 浏览器版本相匹配的 ChromeDriver 版本进行下载。

下载地址:googlechromelabs.github.io/chrome-for-…

chromedriver

  • 解压:下载完成后,解压 ChromeDriver 到一个你记得的目录。

  • 配置环境变量(可选)

    • Windows 上,将 ChromeDriver路径添加到系统的 PATH 环境变量中。
    • MacLinux 上,可以在终端中使用 export PATH=$PATH:/path/to/chromedriver 命令,将 ChromeDriver 的路径添加到 PATH 环境变量中。也可以将这行命令添加到 .bashrc.bash_profile 文件中,以便每次打开终端时自动设置环境变量。

Selenium基础

Selenium对象和方法概览

Selenium 的核心对象是 WebDriver,它提供了与浏览器交互的方法。以下是一些基本的 Selenium 对象和方法:

对象或方法说明
WebDriver用于创建 WebDriver 实例,核心对象,与浏览器交互。
find_element_by_*用于定位页面元素,例如 find_element_by_idfind_element_by_name 等。
find_elements_by_*返回所有匹配的元素。
get加载指定的 URL
current_url获取当前页面的 URL
title获取当前页面的标题
back后退到浏览器历史记录中的上一个页面。
forward前进到浏览器历史记录中的下一个页面。
refresh刷新当前页面。
close关闭当前窗口。
quit关闭所有窗口,并结束 WebDriver 会话。

浏览器基本操作

  • 使用 webdriver.Chrome() 方法来打开浏览器。
  • 使用 get(URL) 方法导航到页面。
  • 使用 quit() 关闭浏览器。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

# 等待几秒钟以观察效果
time.sleep(10)

# 完成后关闭浏览器
driver.quit()

查看页面元素

查看页面元素对前端来说是小菜一碟,我们可以通过 IDTag NameClassXPathCSS Selector 等定位元素。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

# 通过标签名定位
tag_element = driver.find_element(By.TAG_NAME, 'nav')
print('@@@ 通过标签名定位', tag_element)
# 输出:@@@ 通过标签名定位 <selenium.webdriver.remote.webelement.WebElement (session="ffe53878aee56d8bbc922cbdad818380", element="f.495E49F864D91F7A7087BA0622B7C8D6.d.F0348429DF22E18BA211FD04F7F18038.e.19")>

# 通过ID定位
id_element = driver.find_element(By.ID, 'js-flash-container')
print('@@@ 通过ID定位', id_element)
# 输出:@@@ 通过ID定位 <selenium.webdriver.remote.webelement.WebElement (session="f1cfb9ee35f2481612bb128c37685cef", element="f.DDDA952C195AFCAD3B0A84E3A0F35B1C.d.CCA8F91E0BA7467AF81CCED217733054.e.19")>

# 通过Class定位
class_element = driver.find_element(By.CLASS_NAME, 'color-bg-subtle')
print('@@@ 通过Class定位', class_element)
# 输出:@@@ 通过Class定位 <selenium.webdriver.remote.webelement.WebElement (session="f1cfb9ee35f2481612bb128c37685cef", element="f.DDDA952C195AFCAD3B0A84E3A0F35B1C.d.CCA8F91E0BA7467AF81CCED217733054.e.20")>

# 等待几秒钟以观察效果
time.sleep(10)

# 完成后关闭浏览器
driver.quit()

页面元素操作

  • 获取元素文本:使用 element.text 属性。
  • 点击:使用 element.click() 方法。
  • 输入文本:使用 element.send_keys(text) 方法。
  • 提交表单:使用 element.submit() 方法。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

# 通过Class定位
class_element = driver.find_element(By.CLASS_NAME, 'color-bg-subtle')
# 获取元素文本
print(class_element.text)
# 输出:
# Topics
# Browse popular topics on GitHub.

# 点击
search_element = driver.find_element(By.CLASS_NAME, 'header-search-button')
search_element.click()

# 输入文本
input_element = driver.find_element(By.ID, 'query-builder-test')
input_element.send_keys('hello')

time.sleep(2)

# 提交表单
input_element.submit()

# 等待几秒钟以观察效果
time.sleep(10)

# 完成后关闭浏览器
driver.quit()

录入文本效果如下图:

crawler-1

提交表单后效果如下图:

crawler-2

高级操作

等待机制

  • 显式等待(WebDriverWait)显式等待允许你指定等待某个条件成立后再继续执行代码。这是处理动态内容加载非常有效的方式。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

try:
  # 显式等待(WebDriverWait)
  element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "query-builder-test"))
  )
finally:
  # 等待几秒钟以观察效果
  time.sleep(10)

  # 完成后关闭浏览器
  driver.quit()
  • 隐式等待:隐式等待设置了一个全局等待时间Selenium 会等待直到指定的时间结束或者找到元素为止。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)
driver.implicitly_wait(5)  # 等待最多5秒

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

# 等待几秒钟以观察效果
time.sleep(10)

# 完成后关闭浏览器
driver.quit()

滚动和定位可视区域

使用 execute_script 去执行 window.scrollToelement.scrollIntoView() 去滚动到固定位置或者元素。

滚动到某个元素

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)
driver.implicitly_wait(5)  # 等待最多5秒

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

# 滚动到元素
footer_element = driver.find_element(By.CLASS_NAME, 'footer')
driver.execute_script("arguments[0].scrollIntoView();", footer_element)

# 等待几秒钟以观察效果
time.sleep(10)

# 完成后关闭浏览器
driver.quit()

滚动到固定位置

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)
driver.implicitly_wait(5)  # 等待最多5秒

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

time.sleep(2)

# 滚动Y轴到250px
driver.execute_script("window.scrollTo(0, 250);")

time.sleep(2)

# 滚动Y轴到650px
driver.execute_script("window.scrollTo(0, 650);")

time.sleep(2)

# 滚动Y轴到0px
driver.execute_script("window.scrollTo(0, 0);")

# 等待几秒钟以观察效果
time.sleep(5)

# 完成后关闭浏览器
driver.quit()

处理Cookies

Selenium 中,Cookies 是一种用于在浏览器中存储用户信息的机制。它们通常用于保持用户登录状态、跟踪用户行为等。

操作Cookies的方法

  • 获取Cookies:使用 driver.get_cookies() 方法获取当前页面的所有Cookies。
  • 获取单个Cookie:使用 driver.get_cookie(name) 方法获取一个Cookie。
  • 添加单个Cookie:使用 driver.add_cookie(cookie_dict) 方法添加一个Cookie。
  • 修改单个Cookie:使用 driver.add_cookie(cookie_dict) 方法修改一个Cookie。
  • 删除单个Cookie:使用 driver.delete_cookie(name) 方法删除一个Cookie。
  • 删除所有Cookies:使用 driver.delete_all_cookies() 方法删除所有Cookies。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

# 获取所有cookies
cookies = driver.get_cookies()
print('@@@ 获取所有cookies', cookies)
# 输出:@@@ 获取cookies [{'domain': '.github.com', 'expiry': 1765019116, 'httpOnly': True, 'name': 'logged_in', 'path': '/', 'sameSite': 'Lax', 'secure': True, 'value': 'no'}, {'domain': '.github.com', 'expiry': 1765019116, 'httpOnly': False, 'name': '_octo', 'path': '/', 'sameSite': 'Lax', 'secure': True, 'value': 'GH1.1.31591717.1733483116'}, {'domain': '.github.com', 'httpOnly': False, 'name': 'tz', 'path': '/', 'sameSite': 'Lax', 'secure': True, 'value': 'Asia%2FShanghai'}, {'domain': '.github.com', 'httpOnly': False, 'name': 'preferred_color_mode', 'path': '/', 'sameSite': 'Lax', 'secure': True, 'value': 'light'}, {'domain': '.github.com', 'httpOnly': False, 'name': 'cpu_bucket', 'path': '/', 'sameSite': 'Lax', 'secure': True, 'value': 'md'}, {'domain': 'github.com', 'httpOnly': True, 'name': '_gh_sess', 'path': '/', 'sameSite': 'Lax', 'secure': True, 'value': 'PoRbFihouKYXdyt2mjxNtNn0pPTtfrhoSduAn8MRASRmODZG9gpwBuTHT8Lt50Zf0POJBToR8W%2B02PCtYS4eu%2B5UwCP0KNbX8olxQ8C5gnJOx9gAs%2BhITgWL69UeCfMNFcI01eYJJ79tWh8eCLwkK536kJxM0kG6XXnmK5Zoj%2FPlH%2BVvElfJpAD7X0BFJ8GtZ4VYEe53cd8RWQYuPQ81m60kYBcnNyo%2BY%2BLdi5BafxVE8mAUYCrGIbXu1%2BDdz%2BvoIjgfYiGtiFS6QdnYwcw5yA%3D%3D--AKNcVUGMAiLpSyPi--He8MHb1zSfqYj4YW3aIYKw%3D%3D'}]

# 获取单个Cookie
cookie_logged_in = driver.get_cookie('logged_in')
print('@@@ 获取单个cookie', cookie_logged_in)
# 输出:@@@ 获取单个cookie {'domain': '.github.com', 'expiry': 1765019385, 'httpOnly': True, 'name': 'logged_in', 'path': '/', 'sameSite': 'Lax', 'secure': True, 'value': 'no'}

# 添加单个Cookie
driver.add_cookie({"name": "log", "value": "日志"})
log_cookie = driver.get_cookie('log')
print(log_cookie)
# 输出:{'domain': 'github.com', 'httpOnly': False, 'name': 'log', 'path': '/', 'sameSite': 'Lax', 'secure': True, 'value': '日志'}

# 修改单个Cookie
log_cookie['value'] = '@@@@日志22222'
driver.add_cookie(log_cookie)
print(driver.get_cookie('log'))
# 输出:{'domain': '.github.com', 'httpOnly': False, 'name': 'log', 'path': '/', 'sameSite': 'Lax', 'secure': True, 'value': '@@@@日志22222'}

# 删除单个Cookie
driver.delete_cookie("log")
print(driver.get_cookie('log'))
# 输出:None

# 删除全部Cookies
driver.delete_all_cookies()
print(driver.get_cookies())
# 输出:[]

# 等待几秒钟以观察效果
time.sleep(10)

# 完成后关闭浏览器
driver.quit()

异常处理

在使用 Selenium 进行自动化测试时,异常处理是一个重要的环节,它可以帮助我们确保测试脚本的稳定性和可靠性。

常见Selenium异常

  • NoSuchElementException:当 Selenium 尝试查找页面上不存在的元素时触发。
  • StaleElementReferenceException:当元素在查找后被页面重新加载或刷新,导致元素引用失效时触发。
  • TimeoutException:当 WebDriver 等待某个条件满足的时间超过设定的超时时间时触发。
  • ElementNotInteractableException:当元素不可交互(如被遮挡或不可见)时触发。
  • InvalidSelectorException:使用无效的选择器查找元素时抛出。
  • InvalidElementStateException:尝试在元素状态不允许的情况下执行操作时抛出

异常捕获和处理

使用 try-except 语句捕获并处理异常,确保测试脚本的稳定运行。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException

import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

try:
  # 尝试执行的代码,可能抛出异常
  element = driver.find_element(By.ID, "non-existent-element")
  element.click()
except NoSuchElementException:
  # 处理NoSuchElementException异常
  print("元素未找到")
except Exception as e:
  # 处理其他类型的异常
  print("发生其他异常:", e)
finally:
  # 关闭浏览器
  driver.quit()

# 输出:元素未找到

数据提取与解析

在网络爬虫中,数据提取和解析是核心任务之一。

提取HTML内容

可以使用 page_source 属性来获取当前页面的 HTML 内容。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

# 获取页面的HTML内容
html_content = driver.page_source

# 打印HTML内容
print(html_content)
# 输出:<html> xxx </html>

# 等待几秒钟以观察效果
time.sleep(10)

# 完成后关闭浏览器
driver.quit()

使用BeautifulSoup进行HTML解析

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from bs4 import BeautifulSoup

import time

# 如果配置了全局就不需要写chrome_driver_path
chrome_driver_path = '../chromedriver-mac-x64/chromedriver'

# 创建 Service 对象
service = Service(executable_path=chrome_driver_path)

# 打开浏览器
# 如果配置了全局就不需要写service
driver = webdriver.Chrome(service=service)

# 使用driver来控制Chrome浏览器了
driver.get('https://github.com/topics')

# 获取页面的HTML内容
html_content = driver.page_source

soup = BeautifulSoup(html_content, 'html.parser')

# 提取标题
title = soup.find('title').text

# 提取所有链接
links = soup.find_all('a')
for link in links:
  print(link.get('href'))

# 提取特定类名的元素
elements = soup.find_all(class_='topic-box')
for element in elements:
  print(element.text)

# 等待几秒钟以观察效果
time.sleep(10)

# 完成后关闭浏览器
driver.quit()

JSON数据提取

如果 JSON 数据是通过 JS 动态加载的,需要使用 Selenium 等待数据加载完成,然后使用 execute_script 方法来获取 JS 变量的值。

# 假设页面中有一个JavaScript变量var myData = {name: "niunai", age: 33, city: "上海"};

# 使用execute_script获取JSON数据
json_data = driver.execute_script("return myData;")

# 将获取的数据转换为Python字典
data = json.loads(json_data)

# 访问数据
print(data['name'])  # 输出: niunai

法律和道德问题

在使用 Selenium 进行网络爬虫自动化测试时,遵守法律和道德规范是非常重要的。

遵守Robots协议

  • Robots协议:这是一个网站通过 robots.txt 文件提供的指导,告诉爬虫哪些页面可以访问,哪些页面不应该访问。robots.txt 通常位于网站的根目录下,例如:https://www.baidu.com/robots.txt
  • 遵守协议:在使用 Selenium 时,应该检查并遵守目标网站的 robots.txt 文件。不要尝试访问被禁止的页面或目录。

尊重版权和隐私

  • 版权:在爬取和使用网站内容时,必须尊重版权法。不要复制、分发或使用受版权保护的内容,除非你已经获得了版权所有者的明确许可。
  • 隐私:尊重用户的隐私权。不要收集、存储或使用用户的个人信息,除非已经获得了用户的同意,并且符合相关的隐私保护法规。

避免对网站造成过大负担

  • 请求频率:不要发送过多的请求到同一个网站,这可能会导致服务器过载,影响网站的正常运行。合理设置请求间隔,以减轻对网站服务器的压力。
  • 分布式爬取:如果需要爬取大量数据,考虑使用分布式爬虫系统,分散请求负载。
  • 服务器时间:尽量避免在网站的高峰时段进行爬取,以减少对网站性能的影响。

申明

本文爬取的是 https://github.com/topics 页面,仅供学习测试,请勿用于非法用途。

python学习专栏系列

练习代码库地址

python-study