MST

星途 面试题库

面试题:Python网络爬虫架构设计与Scrapy库优化

假设要开发一个大规模的网络爬虫,目标是爬取多个网站的新闻内容并进行结构化存储。请设计一个基于Python Scrapy库的爬虫架构,包括数据抓取、解析、存储流程,并阐述如何对Scrapy进行优化以提高爬取效率、应对反爬虫机制。
17.3万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试

1. 基于Scrapy库的爬虫架构设计

数据抓取

  1. 创建Scrapy项目:使用scrapy startproject news_crawler命令创建一个新的Scrapy项目。
  2. 定义Spider:在spiders目录下创建一个或多个Spider类,例如NewsSpider。在Spider类中定义起始URL列表start_urls,并实现parse方法来处理下载的页面。
import scrapy


class NewsSpider(scrapy.Spider):
    name = 'news'
    start_urls = [
        'http://example.com/news',
        'http://another-example.com/news'
    ]

    def parse(self, response):
        # 后续在此处理页面
        pass

数据解析

  1. XPath或CSS选择器:使用XPath或CSS选择器来定位新闻标题、正文、发布时间等信息。例如,假设新闻标题在<h1 class="news-title">标签中,可以使用CSS选择器response.css('.news-title::text').get()获取标题文本。
  2. 正则表达式:对于一些复杂的文本提取场景,可能需要使用正则表达式。例如,从一段包含时间的文本中提取发布时间。
import re


class NewsSpider(scrapy.Spider):
    #...
    def parse(self, response):
        title = response.css('.news-title::text').get()
        time_pattern = re.compile(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})')
        time_text = response.css('.news-time::text').get()
        publish_time = re.search(time_pattern, time_text).group(1) if time_text else None
        yield {
            'title': title,
            'publish_time': publish_time
        }

数据存储

  1. Item Pipeline:定义Item Pipeline来处理爬取到的数据存储。例如,将数据存储到MySQL数据库,可以使用mysql - connector - python库。
import mysql.connector


class MySQLPipeline:
    def __init__(self, host, user, password, database):
        self.host = host
        self.user = user
        self.password = password
        self.database = database

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            host=crawler.settings.get('MYSQL_HOST'),
            user=crawler.settings.get('MYSQL_USER'),
            password=crawler.settings.get('MYSQL_PASSWORD'),
            database=crawler.settings.get('MYSQL_DATABASE')
        )

    def open_spider(self, spider):
        self.connection = mysql.connector.connect(
            host=self.host,
            user=self.user,
            password=self.password,
            database=self.database
        )
        self.cursor = self.connection.cursor()

    def close_spider(self, spider):
        self.cursor.close()
        self.connection.close()

    def process_item(self, item, spider):
        insert_query = "INSERT INTO news (title, publish_time) VALUES (%s, %s)"
        self.cursor.execute(insert_query, (item['title'], item['publish_time']))
        self.connection.commit()
        return item
  1. 配置Item Pipeline:在settings.py文件中启用定义好的Item Pipeline。
ITEM_PIPELINES = {
    'news_crawler.pipelines.MySQLPipeline': 300,
}

2. Scrapy优化以提高爬取效率

  1. 并发设置:在settings.py中调整CONCURRENT_REQUESTS参数,控制同时发送的请求数。例如,设置为CONCURRENT_REQUESTS = 32,可以根据服务器性能和目标网站的承受能力适当调整。
  2. 下载延迟:通过设置DOWNLOAD_DELAY参数,给每个请求添加一定的下载延迟,避免对目标网站造成过大压力,同时也有助于避免被封IP。例如,DOWNLOAD_DELAY = 1表示每个请求之间间隔1秒。
  3. 启用缓存:使用scrapy - cache插件,启用请求缓存,对于已经请求过的URL,直接从缓存中读取数据,减少重复请求。在settings.py中配置HTTPCACHE_ENABLED = True,并可以设置缓存过期时间等参数。

3. 应对反爬虫机制

  1. 随机User - Agent:使用scrapy - fake - useragent库,在每次请求时随机更换User - Agent,模拟不同的浏览器访问。在settings.py中配置:
DOWNLOADER_MIDDLEWARES = {
  'scrapy_fake_useragent.middleware.RandomUserAgentMiddleware': 400,
}
  1. IP代理:维护一个IP代理池,当检测到IP被封时,及时切换到其他可用的代理IP。可以使用scrapy - proxy - middleware库,并结合一些代理IP供应商提供的API来获取代理IP。在settings.py中配置代理中间件:
DOWNLOADER_MIDDLEWARES = {
  'scrapy_proxy_middleware.ProxyMiddleware': 543,
}
  1. 验证码处理:对于出现验证码的情况,可以使用第三方的验证码识别服务,如Tesseract(需要进行训练提高识别准确率)或一些商业的验证码识别API,在检测到验证码时,调用相应服务进行识别并填写。
  2. 模拟登录:如果部分网站需要登录才能访问新闻内容,可以使用scrapy - selenium库,结合Selenium和浏览器驱动(如ChromeDriver)模拟用户登录操作,获取登录后的Cookie,后续请求携带Cookie进行访问。