电子商务企业只需几行代码,即可监控各店铺页面,排查可能导致残障顾客浏览或购买困难的网页无障碍问题。
网页无障碍对在线零售商至关重要:其一,若顾客无法轻松查找或购买商品,将直接影响销售;其二,鉴于美国网页无障碍法规尚不明确,今年已引发多起诉讼与和解。因此,确保商店具备无障碍功能是规避法律风险的必要举措。
电商企业主可通过多种方案监控网站并发现潜在无障碍问题。本文将介绍如何结合网络爬取框架 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 由三部分组成:
- API 基础地址:
http://wave.webaim.org/api/request? - API 密钥(从 WAVE 仪表板获取):
key={YOURKEYHERE} - 待测页面 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 |

