大数跨境

使用 Scrapy 和 WAVE 监控电商网站的可访问性错误

使用 Scrapy 和 WAVE 监控电商网站的可访问性错误 电商实践
2016-02-19
1
导读:只需几行代码,电子商务企业就可以监控各个店铺页面,查找可能导致网页无障碍访问问题的潜在缺陷

电子商务企业只需几行代码,即可监控各店铺页面,排查可能导致残障顾客浏览或购买困难的网页无障碍问题。

网页无障碍对在线零售商至关重要:其一,若顾客无法轻松查找或购买商品,将直接影响销售;其二,鉴于美国网页无障碍法规尚不明确,今年已引发多起诉讼与和解。因此,确保商店具备无障碍功能是规避法律风险的必要举措。

电商企业主可通过多种方案监控网站并发现潜在无障碍问题。本文将介绍如何结合网络爬取框架 Scrapy 与无障碍测试 API,自动爬取自有电商网站并定位无障碍错误。

注册 WAVE API

编写爬虫前,需先注册 Web Accessibility in Mind (WebAIM) WAVE API。该 API 依据万维网联盟(W3C)《Web 内容无障碍指南》(WCAG)及美国《劳动力康复法》第 508 条,评估提交页面是否存在潜在错误。

WAVE API 为付费服务,按信用额度计费。单次请求消耗 2-3 个信用额度,单价约 2.5 至 4 美分(具体取决于购买量)。注册登录后可获取 API 密钥。

爬虫任务目标

本爬虫旨在生成如下报告,展示目标网站(即您的电商网站)每个页面的潜在无障碍错误数量:

url 警报 错误 对比度
https://businessideadaily.com/auth/login 3 3 1
https://businessideadaily.com/password/email 2 2 0
https://businessideadaily.com/ 9 8 3

为实现此目标,爬虫需完成三项核心任务:

  • 发现目标网站几乎所有页面;
  • 将每个页面的 URL 传递给 WAVE API;
  • 捕获无障碍评估结果并填入报告字段。

基于链接提取的基础爬虫

此前文章《使用 Python 和 Scrapy 爬取您的电商网站》已详解如何构建 Scrapy 爬虫以遍历网站所有页面。本文将在该基础上进行扩展,以完成上述第一项任务。

爬虫环境准备

若您已按前述文章创建爬虫,可直接沿用;否则,可使用以下命令生成新爬虫:

scrapy genspider -t crawl testaccess businessideadaily.com

该命令将生成 testaccess.py 文件,初始代码如下:

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from bid.items import BidItem

class TestaccessSpider(CrawlSpider):
    name = 'testaccess'
    allowed_domains = ['businessideadaily.com']
    start_urls = ['http://www.businessideadaily.com/']
    rules = (
        Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
    )
    def parse_item(self, response):
        i = BidItem()
        #i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
        #i['name'] = response.xpath('//div[@id="name"]').extract()
        #i['description'] = response.xpath('//div[@id="description"]').extract()
        return i

首先导入必要的库:

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from bid.items import BidItem

由于 WAVE API 默认返回 JSON 格式结果,需额外导入 json 库用于解析:

import json

此时 testaccess.py 文件头部应为:

# -*- coding: utf-8 -*-
import json
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from bid.items import BidItem

其次,需在允许域名列表中添加 API 域名 wave.webaim.org,否则 Scrapy 将无法获取无障碍报告:

allowed_domains = ['businessideadaily.com', 'wave.webaim.org']

最后,移除规则中的 allow 参数,以确保爬虫能遍历站点所有页面(若使用前述文章爬虫,此步可能已完成):

Rule(LinkExtractor(), callback='parse_item', follow=True),

收集与处理页面

爬虫通过 parse_item 方法处理响应。当发现新页面时,该方法接收包含页面信息的响应对象。我们将利用此方法收集 URL、构造 API 请求,并将修改后的 URL 传递给下一处理方法。建议重命名该方法以提升可读性(如改为 process_page),但需同步更新规则中的回调引用。

def parse_item(self, response):
    test_url = "http://wave.webaim.org/api/request?key={YOURKEYHERE}&url=" + response.url
    yield scrapy.Request(test_url, callback=self.parse_test_json)

请求 URL 由三部分组成:

  1. API 基础地址:http://wave.webaim.org/api/request?
  2. API 密钥(从 WAVE 仪表板获取):key={YOURKEYHERE}
  3. 待测页面 URL:response.url

拼接后的完整请求示例:

http://wave.webaim.org/api/request?key=kdh76h83&url=http://businessideadaily.com

通过 yield scrapy.Request() 发起请求,并指定 parse_test_json 作为回调函数接收无障碍报告。

解析无障碍报告

parse_test_json 方法负责整理 WAVE API 响应,提取关键数据:

def parse_test_json(self, response):
    test_results = json.loads(response.body_as_unicode())
    href = BidItem()
    href['url'] = test_results['statistics']['pageurl']
    href['errors'] = test_results['categories']['error']['count']
    href['contrast'] = test_results['categories']['contrast']['count']
    href['alerts'] = test_results['categories']['alert']['count']
    href['report'] = test_results
    return href

其中 test_results 为解析后的 JSON 数据,示例结构如下:

{
  "status": {"success": true},
  "statistics": {
    "waveurl": "http://wave.webaim.org/report?url=https://businessideadaily.com/",
    "pagetitle": "Business Idea Daily",
    "pageurl": "https://businessideadaily.com/",
    "allitemcount": 51,
    "creditsremaining": 90,
    "time": "1.38",
    "totalelements": 142
  },
  "categories": {
    "feature": {"count": 1, "description": "Features"},
    "contrast": {"count": 3, "description": "Contrast Errors"},
    "html5": {"count": 15, "description": "HTML5 and ARIA"},
    "error": {"count": 8, "description": "Errors"},
    "alert": {"count": 9, "description": "Alerts"},
    "structure": {"count": 15, "description": "Structural Elements"}
  }
}

需提取五项数据:完整 JSON 结果、错误数、对比度错误数、警报数及被测页面 URL。这些数据将存入 Item 中。需在 items.py 文件中扩展字段定义:

class BidItem(scrapy.Item):
    url = scrapy.Field()
    errors = scrapy.Field()
    contrast = scrapy.Field()
    alerts = scrapy.Field()
    report = scrapy.Field()

在 testaccess.py 中为各字段赋值时,注意通过层级路径获取计数值,例如:

href['errors'] = test_results['categories']['error']['count']

最终返回填充完整的 Item:

return href

执行爬取与查看结果

进入爬虫目录后,运行以下命令启动爬取并导出 CSV 报告:

scrapy crawl testaccess -o webaccess.csv

生成的 webaccess.csv 可用 Google Sheets 等工具打开,展示各页面的错误、对比度问题及警报数量,示例如下:

报告 URL 警报 错误 对比度
{"status": {"success": true}, "statistics": {"waveurl": "...", "pageurl": "https://businessideadaily.com/password/email", ...}, "categories": {"error": {"count": 2}, "alert": {"count": 2}, "contrast": {"count": 0}}} https://businessideadaily.com/password/email 2 2 0
{"status": {"success": true}, "statistics": {"waveurl": "...", "pageurl": "https://businessideadaily.com/", ...}, "categories": {"error": {"count": 8}, "alert": {"count": 9}, "contrast": {"count": 3}}} https://businessideadaily.com/ 9 8 3
【声明】内容源于网络
0
0
电商实践
电商实践
内容 8053
粉丝 0
电商实践 电商实践
总阅读37.4k
粉丝0
内容8.1k