Python3 爬虫教程:Ajax 数据爬取实战

1. 准备工作

在开始本节内容前,请确保:

  • 已安装 Python 3.6 或更高版本
  • 熟悉 requests 库的基本用法
  • 了解 Ajax 基本原理和分析方法

2. 目标网站分析

我们将爬取示例网站:https://spa1.scrape.center/

网站特点:

  • 数据通过 Ajax 加载
  • 页面内容由 JavaScript 渲染
  • 支持分页浏览
  • 包含电影详情页

需要爬取的数据:

  • 电影名称
  • 封面图片
  • 类别
  • 上映日期
  • 评分
  • 剧情简介

3. 初步探索

使用 requests 直接获取页面源码:

import requests

url = 'https://spa1.scrape.center/'
response = requests.get(url)
print(response.text)

结果发现只获取到少量 HTML 内容,没有电影数据,说明数据是通过 JavaScript 动态加载的。

4. 爬取列表页

4.1 分析 Ajax 接口

通过浏览器开发者工具分析:

  1. 打开 Network 面板
  2. 勾选 Preserve Log
  3. 切换到 XHR 选项卡
  4. 翻页观察请求

发现列表页接口格式: https://spa1.scrape.center/api/movie/?limit=10&offset=0

参数说明:

  • limit: 每页数据量(固定为10)
  • offset: 数据偏移量(每页增加10)

4.2 实现爬取代码

import requests
import logging

# 基础配置
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')
INDEX_URL = 'https://spa1.scrape.center/api/movie/?limit={limit}&offset={offset}'
LIMIT = 10

def scrape_api(url):
    logging.info('scraping %s...', url)
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.json()
        logging.error('get invalid status code %s while scraping %s', response.status_code, url)
    except requests.RequestException:
        logging.error('error occurred while scraping %s', url, exc_info=True)

def scrape_index(page):
    url = INDEX_URL.format(limit=LIMIT, offset=LIMIT * (page - 1))
    return scrape_api(url)

5. 爬取详情页

5.1 分析详情页接口

详情页 URL 格式: https://spa1.scrape.center/api/movie/{id}/

其中 id 可从列表页数据中获取。

5.2 实现爬取代码

DETAIL_URL = 'https://spa1.scrape.center/api/movie/{id}/'

def scrape_detail(id):
    url = DETAIL_URL.format(id=id)
    return scrape_api(url)

6. 数据存储(MongoDB)

6.1 配置 MongoDB

import pymongo

# MongoDB 配置
MONGO_CONNECTION_STRING = 'mongodb://localhost:27017'
MONGO_DB_NAME = 'movies'
MONGO_COLLECTION_NAME = 'movies'

client = pymongo.MongoClient(MONGO_CONNECTION_STRING)
db = client[MONGO_DB_NAME]
collection = db[MONGO_COLLECTION_NAME]

6.2 实现数据存储

def save_data(data):
    collection.update_one({
        'name': data.get('name')
    }, {
        '$set': data
    }, upsert=True)

7. 完整实现

def main():
    for page in range(1, 11):
        index_data = scrape_index(page)
        for item in index_data.get('results'):
            id = item.get('id')
            detail_data = scrape_detail(id)
            logging.info('detail data %s', detail_data)
            save_data(detail_data)
            logging.info('data saved successfully')

if __name__ == '__main__':
    main()

8. 现代爬虫技术改进建议

  1. 异步爬取:使用 aiohttp 替代 requests 提高效率
  2. 错误处理:增加重试机制和代理池
  3. 反反爬:添加随机 User-Agent 和请求间隔
  4. 容器化:使用 Docker 部署爬虫
  5. 分布式:结合 Scrapy-Redis 实现分布式爬取

9. 总结

通过本教程我们学习了:

  1. 分析 Ajax 数据接口
  2. 爬取分页列表数据
  3. 获取详情页信息
  4. 存储到 MongoDB 数据库
  5. 现代爬虫技术改进方向

10.完整代码

import requests
import logging
import pymongo

# 基础配置
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')
INDEX_URL = 'https://spa1.scrape.center/api/movie/?limit={limit}&offset={offset}'
LIMIT = 10
DETAIL_URL = 'https://spa1.scrape.center/api/movie/{id}/'
# MongoDB 配置
MONGO_CONNECTION_STRING = 'mongodb://localhost:27017'
MONGO_DB_NAME = 'movies'
MONGO_COLLECTION_NAME = 'movies'

client = pymongo.MongoClient(MONGO_CONNECTION_STRING)
db = client[MONGO_DB_NAME]
collection = db[MONGO_COLLECTION_NAME]


def scrape_api(url):
    logging.info('scraping %s...', url)
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.json()
        logging.error('get invalid status code %s while scraping %s', response.status_code, url)
    except requests.RequestException:
        logging.error('error occurred while scraping %s', url, exc_info=True)

def scrape_index(page):
    url = INDEX_URL.format(limit=LIMIT, offset=LIMIT * (page - 1))
    return scrape_api(url)



def scrape_detail(id):
    url = DETAIL_URL.format(id=id)
    return scrape_api(url)



def save_data(data):
    collection.update_one({
        'name': data.get('name')
    }, {
        '$set': data
    }, upsert=True)

def main():
    for page in range(1, 11):
        index_data = scrape_index(page)
        for item in index_data.get('results'):
            id = item.get('id')
            detail_data = scrape_detail(id)
            logging.info('detail data %s', detail_data)
            save_data(detail_data)
            logging.info('data saved successfully')

if __name__ == '__main__':
    main()