Scrapy框架实战:大规模爬取华为应用市场应用详情数据

简介: Scrapy框架实战:大规模爬取华为应用市场应用详情数据

在移动互联网时代,应用商店(App Store)汇聚了海量的应用数据,这些数据对于市场分析、竞品研究、用户行为洞察乃至投资决策都具有无可估量的价值。华为应用市场作为全球Top 3的应用分发平台,其数据更是开发者、分析师和企业所关注的焦点。
手动收集这些数据无异于大海捞针,而Python爬虫技术则是实现自动化、大规模数据采集的利器。在众多Python爬虫框架中,Scrapy 以其强大的功能、高效的异步处理和清晰的项目结构,成为完成此类大规模爬取任务的不二之选。
一、项目目标与准备工作
1.1 爬取目标
我们的目标是爬取华为应用市场上指定分类(如“游戏”、“商务”)中的应用列表,并逐个获取每个应用的详细字段,包括但不限于:
● 应用名称
● 应用包名 (Package Name)
● 开发者
● 评分与评分人数
● 应用大小
● 更新日期
● 应用简介
● 应用截图URL
● 最新版本号
1.2 环境与工具
● Python 3.8+
● Scrapy 2.5+: 使用 pip install scrapy 安装
● 浏览器开发者工具 (F12): 用于分析网络请求和目标数据结构。
1.3 核心思路分析
与一些静态网页不同,现代应用商店的数据通常通过异步API(XHR)接口动态加载。直接解析HTML不仅复杂,而且容易因前端改动而失效。更高效、稳定的方式是直接模拟浏览器调用后端API的行为。

  1. 打开华为应用市场网页版:进入分类列表页。
  2. 打开浏览器开发者工具 (F12),切换到 Network -> XHR 标签。
  3. 滚动列表页,观察是否有新的XHR请求出现,其中包含了应用列表数据。
  4. 点击一个应用,进入详情页,同样在 Network 中寻找包含详细数据的API请求。
  5. 分析找到的API请求:包括URL、请求头(Headers)、请求参数(Payload)和返回的JSON数据结构。
    通过分析,我们通常能找到一个返回JSON格式列表数据的API和一个返回详细信息的API。本教程将基于此假设进行。
    二、Scrapy项目搭建与核心组件编写
    2.1 创建Scrapy项目
    scrapy startproject huawei_appmarket
    cd huawei_appmarket
    scrapy genspider appmarket "huawei.com"
    这会创建一个名为 huawei_appmarket 的项目和一个名为 appmarket 的爬虫。
    2.2 定义数据模型 (Items.py)
    在 items.py 中,我们定义要爬取的数据结构。这使数据管道路由和导出更加清晰。
    import scrapy

class HuaweiAppmarketItem(scrapy.Item):

# 定义要爬取的字段
collection_name = scrapy.Field()  # 集合名,用于MongoDB
category = scrapy.Field()         # 应用分类
app_name = scrapy.Field()         # 应用名称
package_name = scrapy.Field()     # 包名
developer = scrapy.Field()        # 开发者
rating = scrapy.Field()           # 评分
rating_count = scrapy.Field()     # 评分人数
size = scrapy.Field()             # 应用大小
update_date = scrapy.Field()      # 更新日期
description = scrapy.Field()      # 应用简介
screenshot_urls = scrapy.Field()  # 截图URL列表
version = scrapy.Field()          # 版本号
detail_url = scrapy.Field()       # 详情页URL

2.3 编写爬虫核心逻辑 (Spiders/appmarket.py)
这是爬虫的核心。我们需要重写 start_requests 和 parse 方法。
python
import scrapy
import json
from huawei_appmarket.items import HuaweiAppmarketItem
from urllib.parse import urlencode, quote

class AppmarketSpider(scrapy.Spider):
name = 'appmarket'
allowed_domains = ['huawei.com']

# 代理配置
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"

# 假设我们分析出的列表API基础URL和参数
list_api_url = "https://web-drcn.hispace.dbankcloud.cn/uowap/index?"
query_params = {
    'method': 'internal.getTabDetail',
    'serviceType': '20',
    'reqPageNum': 1,
    'maxResults': 20,
    'uri': '',
    'zone': '',
    'locale': 'zh_CN'
}

# 起始分类(例如游戏)
start_uri = 'gameList_1'

def start_requests(self):
    # 构建初始请求的URL
    params = self.query_params.copy()
    params['uri'] = self.start_uri
    url = self.list_api_url + urlencode(params)

    # 添加必要的请求头,否则可能被拒绝访问
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        'Origin': 'https://appgallery.huawei.com',
        'Referer': 'https://appgallery.huawei.com/',
    }

    # 创建请求并添加代理
    request = scrapy.Request(url, headers=headers, callback=self.parse_list)
    request.meta['proxy'] = f"http://{self.proxyUser}:{self.proxyPass}@{self.proxyHost}:{self.proxyPort}"
    yield request

def parse_list(self, response):
    # 解析API返回的JSON数据
    json_data = json.loads(response.text)
    app_list = json_data.get('layoutData', [])

    # 遍历应用列表
    for app in app_list:
        item = HuaweiAppmarketItem()
        item['category'] = self.start_uri
        item['app_name'] = app.get('name')
        item['detail_url'] = app.get('descUrl') # 详情页URL可能用于后续请求

        # 关键:获取每个应用的唯一标识(如package name或id)
        package_name = app.get('packageName')
        item['package_name'] = package_name

        # 这里假设我们分析出了获取详情的API,需要传入appid
        app_id = app.get('id')
        if app_id:
            # 构建详情API请求 (URL需要根据实际分析结果修改)
            detail_api_url = f"https://web-drcn.hispace.dbankcloud.cn/uowap/index?method=internal.getTabDetail&serviceType=20&appid={app_id}&zone=&locale=zh_CN"
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                'Referer': 'https://appgallery.huawei.com/'
            }
            # 创建请求并添加代理
            request = scrapy.Request(detail_api_url, headers=headers, callback=self.parse_detail, meta={'item': item})
            request.meta['proxy'] = f"http://{self.proxyUser}:{self.proxyPass}@{self.proxyHost}:{self.proxyPort}"
            yield request

    # 分页逻辑:如果当前页不是最后一页,则请求下一页
    current_page = self.query_params['reqPageNum']
    total_page = json_data.get('totalPage', 1)
    if current_page < total_page:
        self.query_params['reqPageNum'] += 1
        next_params = self.query_params.copy()
        next_params['uri'] = self.start_uri
        next_url = self.list_api_url + urlencode(next_params)

        # 创建请求并添加代理
        request = scrapy.Request(next_url, headers=response.request.headers, callback=self.parse_list)
        request.meta['proxy'] = f"http://{self.proxyUser}:{self.proxyPass}@{self.proxyHost}:{self.proxyPort}"
        yield request

def parse_detail(self, response):
    # 从meta中获取之前初步构建的item
    item = response.meta['item']

    # 解析详情API返回的JSON
    detail_data = json.loads(response.text)
    # 这里是一个示例解析逻辑,实际结构需要根据API返回的JSON调整
    app_detail = detail_data.get('layoutData', [{}])[0] if detail_data.get('layoutData') else {}

    item['developer'] = app_detail.get('developerName')
    item['rating'] = app_detail.get('rating')
    item['rating_count'] = app_detail.get('ratingCount')
    item['size'] = app_detail.get('sizeDesc')
    item['update_date'] = app_detail.get('updateTime')
    item['version'] = app_detail.get('versionName')
    item['description'] = app_detail.get('introduction')

    # 解析截图
    screenshot_urls = []
    medias = app_detail.get('mediaData', [])
    for media in medias:
        if media.get('type') == 'screenshot': # 类型为截图
            screenshot_urls.append(media.get('url', ''))
    item['screenshot_urls'] = screenshot_urls

    yield item

重要提示:上面的API URL和参数结构均为示例,华为应用市场的实际接口可能会频繁变动。请务必使用浏览器开发者工具分析当前有效的接口,并替换代码中的相应部分。核心是掌握这种直接请求JSON API的方法论。
2.4 配置与中间件 (Settings.py)
在 settings.py 中进行关键配置,以提高爬虫的成功率和友善度。

降低爬取速度,遵守robots.txt,避免对服务器造成压力

DOWNLOAD_DELAY = 1
ROBOTSTXT_OBEY = True

启用并配置User-Agent中间件,模拟真实浏览器

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'

启用Item Pipelines,用于后续数据存储

ITEM_PIPELINES = {
'huawei_appmarket.pipelines.HuaweiAppmarketPipeline': 300,
}

可以设置重试和超时

RETRY_TIMES = 2
DOWNLOAD_TIMEOUT = 15
2.5 数据存储管道 (Pipelines.py)
Scrapy处理完数据后,会将其发送到Pipelines进行后续处理(如清洗、存储)。
import pymongo

class HuaweiAppmarketPipeline:
def init(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db

@classmethod
def from_crawler(cls, crawler):
    return cls(
        mongo_uri=crawler.settings.get('MONGO_URI', 'mongodb://localhost:27017'),
        mongo_db=crawler.settings.get('MONGO_DATABASE', 'huawei_appmarket')
    )

def open_spider(self, spider):
    self.client = pymongo.MongoClient(self.mongo_uri)
    self.db = self.client[self.mongo_db]

def close_spider(self, spider):
    self.client.close()

def process_item(self, item, spider):
    # 指定存储的集合(表)名
    collection_name = item.get('collection_name', 'apps')
    # 使用包名作为唯一索引,避免重复插入
    self.db[collection_name].update_one(
        {'package_name': item['package_name']},
        {'$set': dict(item)},
        upsert=True
    )
    return item

在 settings.py 中添加MongoDB配置:
python
MONGO_URI = 'mongodb://localhost:27017'
MONGO_DATABASE = 'huawei_appmarket'
三、运行与总结
3.1 运行爬虫
在项目根目录下,执行以下命令运行爬虫并将日志输出到 run.log:
bash
scrapy crawl appmarket -s LOG_FILE=run.log
3.2 可能遇到的问题与对策
● 反爬虫(403 Forbidden):需要补充更多请求头,如 Origin, Referer,甚至考虑使用代理IP池。
● API变更:这是最大的风险,需要定期检查并更新代码中的API URL和参数。
● 数据解析错误:JSON结构可能微调,需要调整 parse_detail 中的解析逻辑。
● 速率限制:如果被限流,应适当增加 DOWNLOAD_DELAY。
3.3 总结
通过本项目,我们演示了使用Scrapy框架进行大规模数据爬取的标准流程:

  1. 项目分析:使用开发者工具分析API接口。
  2. 环境搭建:创建Scrapy项目与爬虫。
  3. 模型定义:在 items.py 中结构化数据。
  4. 爬虫编写:在Spider中实现核心抓取与解析逻辑(parse_list, parse_detail)。
  5. 配置优化:在 settings.py 中设置爬虫规则。
  6. 数据持久化:在 pipelines.py 中将数据存储到数据库(如MongoDB)。
相关文章
|
1月前
|
数据采集 Web App开发 数据安全/隐私保护
实战:Python爬虫如何模拟登录与维持会话状态
实战:Python爬虫如何模拟登录与维持会话状态
|
3月前
|
数据采集 存储 NoSQL
Scrapy 框架实战:构建高效的快看漫画分布式爬虫
Scrapy 框架实战:构建高效的快看漫画分布式爬虫
|
2月前
|
数据采集 Web App开发 自然语言处理
新闻热点一目了然:Python爬虫数据可视化
新闻热点一目了然:Python爬虫数据可视化
|
3月前
|
数据采集 Web App开发 存储
用Python的Requests+BeautifulSoup爬取微博热搜榜及话题内容
用Python的Requests+BeautifulSoup爬取微博热搜榜及话题内容
|
6月前
|
数据采集 存储 NoSQL
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
365 67
|
2月前
|
数据采集 运维 监控
构建企业级Selenium爬虫:基于隧道代理的IP管理架构
构建企业级Selenium爬虫:基于隧道代理的IP管理架构
|
2月前
|
数据采集 Web App开发 JavaScript
应对反爬:使用Selenium模拟浏览器抓取12306动态旅游产品
应对反爬:使用Selenium模拟浏览器抓取12306动态旅游产品
|
3月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
1月前
|
数据采集 人工智能 JSON
Prompt 工程实战:如何让 AI 生成高质量的 aiohttp 异步爬虫代码
Prompt 工程实战:如何让 AI 生成高质量的 aiohttp 异步爬虫代码
|
JSON API 网络安全
App数据的爬取
App数据的爬取
541 3