网络爬虫是一种自动化、程序化的过程,通过它可以不断地从网页上 "爬取" 数据。网络抓取也称为屏幕抓取或网络采集,可以从任何可公开访问的网页上提供即时数据。在某些网站上,网页抓取可能是非法的。
1: 使用 Scrapy 框架进行抓取
首先,你必须建立一个新的 Scrapy 项目。输入一个存放代码的目录,然后运行:
scrapy startproject stackoverflow_project
要进行抓取,我们需要一个爬虫。爬虫定义了如何对某个网站进行抓取。下面是一个爬虫的代码,它跟踪 StackOverflow 上投票最高的问题的链接,并从每个页面上刮取一些数据(源代码):
import scrapy
class StackOverflowSpider(scrapy.Spider):
name = 'stackoverflow' # 每个爬虫都有一个唯一的名称
start_urls = ['http://stackoverflow.com/questions?sort=votes'] # 爬取的起始 URL
def parse(self, response):
# 使用 CSS 选择器提取问题链接
for href in response.css('.question-summary h3 a::attr(href)'):
full_url = response.urljoin(href.get()) # 构造完整的问题 URL
yield scrapy.Request(full_url, callback=self.parse_question)
# 处理分页,自动翻页
next_page = response.css('.pager .next a::attr(href)').get()
if next_page:
yield response.follow(next_page, self.parse)
def parse_question(self, response):
# 提取问题的详细信息
yield {
'title': response.css('h1 a::text').get(), # 问题标题
'votes': response.css('.question .vote-count-post::text').get(), # 问题的投票数
'body': response.css('.question .post-text').get(), # 问题的正文内容
'tags': response.css('.question .post-tag::text').getall(), # 问题的标签
'link': response.url, # 问题的链接
}
将爬虫类保存在 projectName\spiders 目录中。在这种情况下
stackoverflow_project\stackoverflow_project\spiders\stackoverflow_spider.py.
现在你可以使用你的爬虫了。例如,试着运行(在项目目录下):
cd stackoverflow_project
scrapy crawl stackoverflow -o output.json
这会将爬取的结果保存到 output.json 文件中。
2: 使用 Selenium WebDriver 进行抓取
有些网站不喜欢被爬取数据。在这种情况下,您可能需要模拟使用浏览器的真实用户。Selenium 可启动并控制网络浏览器。
from selenium import webdriver
browser = webdriver.Firefox() # launch Firefox browser
browser.get('http://stackoverflow.com/questions?sort=votes') # load url
title = browser.find_element_by_css_selector('h1').text # page title (first h1 element)
questions = browser.find_elements_by_css_selector('.question-summary') # question list
for question in questions: # iterate over questions
question_title = question.find_element_by_css_selector('.summary h3 a').text
question_excerpt = question.find_element_by_css_selector('.summary .excerpt').text
question_vote = question.find_element_by_css_selector('.stats .vote .votes .vote-countpost').text
print("%s\n%s\n%s votes\n-----------\n" % (question_title, question_excerpt, question_vote))
Selenium 的功能远不止这些。它可以修改浏览器的 cookie、填写表格、模拟鼠标点击、截取网页截图以及运行自定义 JavaScript。
3: 使用 requests 和 lxml 爬取数据的基本示例
# For Python 2 compatibility.
import lxml.html
import requests
def main():
try:
# 发送 HTTP GET 请求
url = "https://httpbin.org"
r = requests.get(url)
# 检查响应状态码
if r.status_code == 200:
html_source = r.text
root_element = lxml.html.fromstring(html_source)
# 使用 XPath 提取页面标题
page_title = root_element.xpath('/html/head/title/text()')
if page_title:
print("Page Title:", page_title[0])
else:
print("No title found on the page.")
else:
print(f"Failed to retrieve the page. Status code: {r.status_code}")
except requests.exceptions.RequestException as e:
print(f"Error occurred while trying to fetch the URL: {e}")
print("Please check the URL's validity and your network connection. Try again later.")
except lxml.etree.ParserError as e:
print(f"Error occurred while parsing the HTML content: {e}")
print("The HTML content may be malformed or incomplete.")
if __name__ == '__main__':
main()
4: 通过 requests 维护网络抓取的会话
维护网络抓取会话是个好主意,可以持久保存 cookie 和其他参数。此外,它会提高性能,因为 requests.Session 会重复使用与主机的底层 TCP 连接:
import requests
with requests.Session() as session:
# all requests through session now have User-Agent header set
session.headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36'}
# set cookies
session.get('http://httpbin.org/cookies/set?key=value')
# get cookies
response = session.get('http://httpbin.org/cookies')
print(response.text)
5: 使用 BeautifulSoup4 进行搜索
from bs4 import BeautifulSoup
import requests
def fetch_codechef_problems():
url = "https://www.codechef.com/problems/easy"
try:
# 发送 HTTP GET 请求
res = requests.get(url, timeout=10) # 设置超时时间,避免长时间等待
res.raise_for_status() # 检查请求是否成功
# 创建 BeautifulSoup 对象
page = BeautifulSoup(res.text, 'lxml') # 使用 lxml 解析器
# 使用 CSS 选择器获取问题列表
datatable_tags = page.select('table.dataTable') # 问题列表在 class="dataTable" 的