当前位置: 首页 > article >正文

Python常见面试题的详解21

1. 说明Scrapy 框架运行的机制

  • 要点

Scrapy 是强大的 Python 爬虫框架,其运行依赖多个核心组件协同工作,这些组件之间通过引擎有序调度,实现数据的高效爬取。主要组件有引擎、调度器、下载器、爬虫、下载器中间件、爬虫中间件和管道。

以下是对各组件及运行流程的详细解释和简单示例代码辅助理解:

python

# 假设这是一个简单的 Scrapy 项目结构
import scrapy
from scrapy import signals
from scrapy.crawler import CrawlerProcess

# 定义一个简单的爬虫
class SimpleSpider(scrapy.Spider):
    name = "simple_spider"
    start_urls = ['http://example.com']

    def parse(self, response):
        # 解析响应内容
        yield {'title': response.css('title::text').get()}

# 以下模拟各组件的交互流程
# 引擎负责协调,这里简化为一个函数
def engine():
    # 创建爬虫实例
    spider = SimpleSpider()
    # 从爬虫获取初始请求
    start_requests = iter(spider.start_requests())
    request = next(start_requests)
    # 模拟调度器调度请求
    scheduler = []
    scheduler.append(request)
    # 模拟下载器下载页面
    def downloader(request):
        # 这里只是简单模拟返回一个响应对象
        class MockResponse:
            def __init__(self):
                self.text = '<html><title>Example Page</title></html>'
        return MockResponse()
    # 从调度器获取请求并交给下载器
    req = scheduler.pop()
    response = downloader(req)
    # 爬虫解析响应
    for item in spider.parse(response):
        print(item)

if __name__ == "__main__":
    engine()

  • 运行流程详细步骤

  1. 引擎获取初始请求:引擎从爬虫的 start_requests 方法获取初始请求。

  2. 调度器调度请求:引擎将请求发送给调度器,调度器对请求进行排序和管理,等待引擎请求时返回。

  3. 下载器下载页面:引擎从调度器获取请求后,将其发送给下载器,下载器根据请求下载网页内容,生成响应并返回给引擎。

  4. 爬虫解析响应:引擎将响应发送给爬虫,爬虫通过 parse 方法解析响应内容,生成新的请求或数据项。

  5. 新请求调度与数据处理:新的请求返回给引擎,再由引擎发送给调度器;数据项则发送给管道进行处理。

2. 如何理解 Scrapy 框架

  • 要点

Scrapy 是基于 Python 的高效,可扩展爬虫框架,它采用模块化设计,将爬虫开发中的网络请求,调度,并发等底层操作封装,开发者只需关注网页解析和数据提取逻辑。同时,它运用异步 I/O 和事件驱动机制,提升了爬取效率,并且提供丰富的中间件和管道机制,方便定制化处理。

python

import scrapy

class MySpider(scrapy.Spider):
    name = "myspider"
    start_urls = ['http://example.com']

    def parse(self, response):
        # 提取网页中的所有链接
        for link in response.css('a::attr(href)').getall():
            yield response.follow(link, self.parse)
        # 提取网页标题
        title = response.css('title::text').get()
        yield {'title': title}

上述代码展示了一个简单的 Scrapy 爬虫,开发者只需定义爬虫类,指定起始 URL 和解析方法,Scrapy 框架会自动处理请求的发送、响应的接收和调度等操作。

3. 如何让 Scrapy 框架发送一个 POST 请求

  • 要点

在 Scrapy 中发送 POST 请求,可通过 scrapy.Request 方法,设置 method 参数为 'POST',并通过 body 参数传递 POST 数据。

以下通过start_requests 方法生成一个 POST 请求,将 JSON 格式的数据通过 body 参数传递,同时设置请求头的 Content-Typeapplication/json。请求发送后,响应会交给 parse 方法处理。

python

import scrapy
import json

class PostSpider(scrapy.Spider):
    name = "post_spider"

    def start_requests(self):
        url = 'https://example.com/api'
        data = {
            'username': 'testuser',
            'password': 'testpass'
        }
        headers = {
            'Content-Type': 'application/json'
        }
        yield scrapy.Request(
            url=url,
            method='POST',
            body=json.dumps(data),
            headers=headers,
            callback=self.parse
        )

    def parse(self, response):
        print(response.text)

4. 怎么判断网站是否更新

  • 要点

判断网站是否更新可以从多个角度入手,如比较内容哈希值、检查更新时间、对比页面元素和使用网站 API。

以下通过计算网页内容的 SHA-256 哈希值,比较当前哈希值和之前记录的哈希值来判断网站是否更新。

python

import hashlib
import requests

def get_content_hash(url):
    response = requests.get(url)
    content = response.text
    hash_object = hashlib.sha256(content.encode())
    return hash_object.hexdigest()

# 假设之前记录的哈希值
previous_hash = 'abc123'
current_url = 'http://example.com'
current_hash = get_content_hash(current_url)

if current_hash != previous_hash:
    print("网站已更新")
else:
    print("网站未更新")

5. 爬取的数据量大概有多大?大概多长时间爬一次?

  • 要点

爬取的数据量和爬取频率受多种因素影响。数据量取决于网站规模、内容复杂度和爬取范围;爬取频率需根据网站更新频率、数据时效性要求和网站反爬机制确定。

  • 示例说明

  1. 数据量:对于小型博客网站,每天可能只产生几 KB 到几十 KB 的文本数据;而大型电商网站,每天可能会产生几百 MB 甚至 GB 级别的商品信息数据。

  2. 爬取频率:新闻网站更新频繁,可设置每小时或每天爬取一次;企业官网更新较慢,可每周或每月爬取一次。以下是一个简单的定时爬取示例:

python

import time
import scrapy
from scrapy.crawler import CrawlerProcess

class MySpider(scrapy.Spider):
    name = "myspider"
    start_urls = ['http://example.com']

    def parse(self, response):
        print(response.text)

process = CrawlerProcess()
while True:
    process.crawl(MySpider)
    process.start()
    time.sleep(86400)  # 每天爬取一次

6. 用什么数据库存储爬下来的数据?怎么部署?

  • 要点

可根据数据特点选择关系型数据库(如 MySQL、PostgreSQL)或非关系型数据库(如 MongoDB、Redis)存储爬取的数据。部署时,需先安装数据库,创建相应的数据库和表结构,再在 Scrapy 项目中配置数据库连接。

以下展示了如何在 Scrapy 项目中使用 MySQL 存储爬取的数据,包括创建表、插入数据和关闭连接等操作。

python

import scrapy
import pymysql

class MySQLPipeline:
    def __init__(self):
        self.connection = pymysql.connect(
            host='localhost',
            user='root',
            password='password',
            database='scrapy_data',
            charset='utf8mb4',
            cursorclass=pymysql.cursors.DictCursor
        )
        self.cursor = self.connection.cursor()
        # 创建表
        create_table_query = """
        CREATE TABLE IF NOT EXISTS items (
            id INT AUTO_INCREMENT PRIMARY KEY,
            title VARCHAR(255)
        )
        """
        self.cursor.execute(create_table_query)
        self.connection.commit()

    def process_item(self, item, spider):
        insert_query = "INSERT INTO items (title) VALUES (%s)"
        self.cursor.execute(insert_query, (item['title'],))
        self.connection.commit()
        return item

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

class MySpider(scrapy.Spider):
    name = "myspider"
    start_urls = ['http://example.com']

    def parse(self, response):
        title = response.css('title::text').get()
        yield {'title': title}


process = CrawlerProcess({
    'ITEM_PIPELINES': {
        '__main__.MySQLPipeline': 300,
    }
})
process.crawl(MySpider)
process.start()

7. 如何实现增量爬取

  • 要点

增量爬取可通过使用哈希值、记录更新时间和使用版本号等方法实现。通过比较新旧数据的特征,只处理有更新的数据。

以下使用 SQLite 数据库存储网页的哈希值,每次爬取时比较当前哈希值和数据库中存储的哈希值,只处理有更新的数据。

python

import scrapy
import hashlib
import sqlite3

class IncrementalSpider(scrapy.Spider):
    name = "incremental_spider"
    start_urls = ['http://example.com']

    def __init__(self):
        self.conn = sqlite3.connect('hashes.db')
        self.cursor = self.conn.cursor()
        self.cursor.execute('CREATE TABLE IF NOT EXISTS hashes (url TEXT, hash TEXT)')
        self.conn.commit()

    def parse(self, response):
        content = response.text
        hash_object = hashlib.sha256(content.encode())
        current_hash = hash_object.hexdigest()
        self.cursor.execute('SELECT hash FROM hashes WHERE url =?', (response.url,))
        result = self.cursor.fetchone()
        if result is None or result[0] != current_hash:
            # 数据有更新
            yield {'url': response.url, 'content': content}
            self.cursor.execute('INSERT OR REPLACE INTO hashes (url, hash) VALUES (?,?)', (response.url, current_hash))
            self.conn.commit()

    def close(self, reason):
        self.conn.close()

8. 爬取下来的数据如何去重,说一下 Scrapy 的具体的算法依据

  • 要点

Scrapy 默认使用 RFPDupeFilter 类实现请求去重,通过计算请求的指纹(对请求的 URL、方法、请求体等信息进行哈希计算),并使用集合存储已处理的指纹,比较新请求的指纹是否存在于集合中来判断是否为重复请求。

以下简单模拟了 Scrapy 中请求指纹的计算过程,通过比较指纹来判断请求是否重复。

python

import hashlib
from scrapy.http import Request

def calculate_fingerprint(request):
    data = f"{request.url}{request.method}{request.body}"
    hash_object = hashlib.sha1(data.encode())
    return hash_object.hexdigest()

request1 = Request(url='http://example.com', method='GET')
request2 = Request(url='http://example.com', method='GET')

fingerprint1 = calculate_fingerprint(request1)
fingerprint2 = calculate_fingerprint(request2)

if fingerprint1 == fingerprint2:
    print("请求重复")
else:
    print("请求不重复")

9. 怎么设置爬取深度

  • 要点

在 Scrapy 中,可通过 settings.py 文件中的 DEPTH_LIMITDEPTH_STATS 配置项设置爬取深度和启用深度统计。

以下DEPTH_LIMIT 设置为 2,表示从起始 URL 开始,最多递归访问两层页面;DEPTH_STATS 启用深度统计,方便开发者了解不同深度的请求数量。

python

import scrapy
from scrapy.crawler import CrawlerProcess

class DepthSpider(scrapy.Spider):
    name = "depth_spider"
    start_urls = ['http://example.com']

    def parse(self, response):
        # 提取网页中的所有链接
        for link in response.css('a::attr(href)').getall():
            yield response.follow(link, self.parse)

process = CrawlerProcess({
    'DEPTH_LIMIT': 2,
    'DEPTH_STATS': True
})
process.crawl(DepthSpider)
process.start()

10. Scrapy 和 Scrapy-Redis 有什么区别?为什么选择 Redis 数据库?

  • 要点

  1. 区别:Scrapy 是单机爬虫框架,适用于小规模项目,请求调度和数据存储在本地;Scrapy-Redis 是基于 Scrapy 的分布式爬虫框架,借助 Redis 实现请求的分布式调度和数据共享,可支持大规模分布式爬取。

  2. 选择 Redis 的原因:Redis 具有高性能、分布式支持、数据结构丰富和持久化等优点,能满足大规模请求的快速调度和处理需求。

python

import scrapy
from scrapy_redis.spiders import RedisSpider

class MyRedisSpider(RedisSpider):
    name = "myredisspider"
    redis_key = 'myspider:start_urls'

    def parse(self, response):
        yield {'title': response.css('title::text').get()}

在 Scrapy-Redis 中,可通过 RedisSpider 类创建分布式爬虫,redis_key 指定从 Redis 中获取起始 URL 的键名。多个爬虫节点可以同时从 Redis 中获取请求并进行处理。



友情提示:本文已经整理成文档,可以到如下链接免积分下载阅读

https://download.csdn.net/download/ylfhpy/90422345


http://www.kler.cn/a/560765.html

相关文章:

  • 【前端开发】能不能用Vue+Bootstrap进行项目开发?有什么需求场景需要用到的地方
  • 一周学会Flask3 Python Web开发-Jinja2模板访问对象
  • 云原生时代的分布式文件系统设计与实现
  • 如何查看PostgreSQL的版本
  • Macos ./ollama目录说明
  • overflow-x: auto 使用鼠标实现横向滚动,区分触摸板和鼠标滚动事件的方法
  • angular简易计算器
  • MybatisPlus-扩展功能-枚举处理器
  • Linux-SaltStack基础
  • 【复习】Redis
  • 1.2 redis7.0.4安装与配置开机自启动
  • cpp的stl二分查找库函数
  • 蓝桥杯备赛-精卫填海-DP
  • Android KMP初探
  • AI驱动的自动化留给人类的时间不多了
  • Unity中点乘和叉乘对于我们来说的作用是什么?
  • VoIP之音频3A技术
  • 五、AIGC大模型_04LLaMA-Factory基础知识与SFT实战
  • 【LeetCodehHot100_0x01】
  • 图像处理案例06 OCR应用