基于Python的网络爬虫在物流信息追踪中的应用
陆承佳
(特灵科技亚太研发中心,江苏 苏州 215400)
摘 要:笔者详细介绍了如何基于Python编程语言开发一个能自动追踪物流信息的网络爬虫工具。该工具可以读取本地文件中的运单号,再通过识别验证码、提交表单、发送请求等操作登录目标网站,最终将采集到的网络数据写入文档,进而帮助用户提高工作效率。
关键词:Python;物流;网络爬虫;数据采集
中图分类号:TP311.1;TP393.092  文献标识码:A  文章编号:1003-9767(2020)12-133-04
The Application of Web Scraping in Tracking Logistics Information
Based on Python
Lu Chengjia
(Trane Technologies Engineering & Technology Center-Asia Pacific, Suzhou Jiangsu 215400, China) Ab
stract: The paper introduces how to develop a web scraping tool to track logistics information automatically based on Python. This tool can read tracking numbers from local files and then login target sites by recognizing CAPTCHA, submitting forms, sending requests and other operations. Finally it will write data collected via the network into documents so as to help users improve work efficiency.
Key words: Python; logistics; web scraping; data collection
0 引言
物流是指物品从供应地向接收地流动的过程,根据具体情况,包括仓储、包装、装卸搬运、运输、流通加工、配送、信息处理等环节。随着经济的飞速发展与分工协作的加深,物流体系不断完善,同时企业的原料供应和产品销售也越来越依赖日益成熟的物流行业。因此,能否及时掌握所有物流信息对维持供应链正常运转起着至关重要的作用。
尽管多数运单状态可通过在对应物流公司网页或应用输入单号直接查得,但为满足原材料、测试样品、销售产品等不同物件的邮寄要求,企业往往不得不同时和多家物流公司合作。这无疑增加了追踪大批量运单物流信息所需的人力成本。本文旨在通过Python程序设计语言编写一个“网络爬虫”(Web Scraping)工具,自动登录各大物流公司查询页面检索并批量抓取目标运单的实时物流信息,从而帮助
用户减少机械、烦琐的手动操作,达到提高工作效率的目的。
1 开发环境简介
Python是一门通用的高级编程语言,目前已凭借其简洁性、易读性以及可扩展性,成为最受欢迎的程序设计语言之一[1]。
Python自带强大的标准库,可以执行系统管理、文档读写、参数编辑等操作。此外,Python社区提供了大量功能丰富的第三方模块,覆盖网络通讯、图形识别、数据处理等各种领域,并且多数成熟而稳定。本文以Python内置的集成开发环境IDLE为平台编写程序,通过引用io、os、requests、PIL、pytesseract、pandas等模块,从本地文件读取运单号,再执行识别验证码图片、提交网页表单、发送登录请求等一系列操作访问目标网页,最终将抓取的最新物流信息写入文档。
2 查询页面访问
Expeditors、DHL及宅急送分别为3家能满足不同寄送条件的物流公司,它们的运单状态查询网页也不尽相同。下文将详细介绍这些网站各自的特点,以及从中采集数据的具体算法。
2.1 请求头定制
鉴于许多网站(如上述的DHL和宅急送)都采取了一
作者简介:陆承佳(1994—),男,上海人,本科,工程师。研究方向:程序设计与软件二次开发。
些“反爬虫”措施以防止脚本工具自动登录网页,故而在利用Python 执行页面访问代码前,应先修改“请求头”(Request Headers )使网络爬虫更像人类用户[2]
。以宅急送为例,右键查询页面进入浏览器“审查元素”(Inspect element ),单击Network 选项卡下的www.zjs/api/tracking.jspx 路径,其请求头如图1
python爬虫开发
所示。
图1 宅急送链接请求头
将图示信息(针对本例,只需Referer 即可)转换为“字典”格式,并在调用requests 模块的get 或post 函数访问网站时将其赋予headers 参数,便能令请求顺利通过。此外,也可先调用Session 函数创建“会话”对象,再使用headers.update 方法为其更新缺省参数——之后在发送登录请求时便不必再重设headers 值。2.2 目标链接锁定
首先,在Expeditors 的搜索栏输入运单号后,可以看到如图2所示的物流信息。
此刻浏览器显示的地址为peditors/expo/ExpoReport/SQGuestDetail.jsp ,但若将其复制到另一标签页则无法复现上图信息。因此,目标数据实际并不在地址栏链接指向的网页中。
点击审查元素并重复查询步骤,可在Network 项最下方到一个peditors/expo/SQGuest?SearchTyp e=eventsSearch&reference=加运单号的链接。尝试将其单独打开,显示内容正是图示物流信息。在程序设计过程中,只需调用requests 模块的get 函数获得该地址指向页面的“响应”
(Response )对象,再通过text 方法即可提取网页文本。
其次,虽然浏览器地址栏显示的DHL 物流信息页面链接包含了运单号,且在重新输入该路径后能再次定位到目标页面,但使用针对Expeditors 的代码却无法抓取实时的运单状态。原因在于,这些数据实际是由AJAX 技术生成的动态内容——AJAX 全称Asynchronous JavaScript and XML (即异步JavaScript 和XML ),它允许JavaScript 创建到远程服务器的HTTP 请求并获得响应,而不必刷新整个网页[3]。
XHR 全称XMLHttpRequest ,是一个用以实现AJAX 功能的JavaScript 应用程序接口(API )。过滤出DHL 运单状态页面Network 选项卡的XHR 类,可发现另一个包含运单号的路径。通过requests 模块的get 函数获得其响应对象后,再调用json 方法解码即可返回加载完成的数据。最后,宅急送的网站结构和Expeditors 、DHL 截然不同,无法直接获取指定运单状态。进入其主页的审查元素可以到如图3所示
的源代码。
图3 宅急送表单源代码
由上述代码可知,必须在正确提交表单(form )后才能查看指定运单状态。而目标链接则为当前页面地址(www.zjs/)和源代码中属性action 值(/api/tracking.jspx )的结合(即已在2.1提及的www.zjs/api/tracking.jspx )。后文将对表单实际要求参数及其提交(post )方法继续展开研究。
3 表单提交
3.1 参数设置
将图3源代码的子项展开后,可知网页表单中应包含运单号和验证码两个参数。其中,运单号部分代码如图4
所示。
图4 运单号源代码
示例代码中的“名称”(name )决定了表单变量名,即在模拟表单提交行为时,运单号变量名称应为orderNos 。同理,根据图5所示代码,验证码变量名则是captcha 。
因为每次刷新页面时网站均会随机生成一张全新的验
图2 Expeditors
运单物流信息
图5 验证码源代码
证码图片,所以为了确保提交的表单验证码与图像相匹配,程序需先利用Session函数创建会话对象——该对象能调用requests模块的所有主要方法,且它发出的各个请求之间保持cookie。
综上所述,网页表单提交代码如下:
s = requests.Session()
header = {
‘Referer’: ‘www.zjs/hwcx/index. jhtml’
}
s.headers.update(header)
code = OCR(s)
params = {
‘orderNos’: trackingNumber,
‘captcha’: code
}
r = s.post(“www.zjs/api/tracking.jspx”, data=params)
)
params字典内的两个值trackingNumber和code分别指用户希望追踪的运单号,以及网站要求填写的验证码——可调用OCR函数自动从图片识别得出,详细代码将在后文进一步介绍。
3.2 图像处理
将图像翻译成文字的过程一般被称作“光学文字识别”(Optical Character Recognition,简称OCR)[2],爬虫脚本也自定义了一个同名的函数以解析验证码信息。OCR函数大致可分为图像处理与文字识别,第一部分的代码如下:image = s.get(“www.zjs/captcha.sv”)
dataStream = t)
captcha = Image.open(dataStream)
captcha = captcha.point(lambda x: 0 if x<1 else 255)
上述程序首先调用会话对象的get方法可获取宅急送网站为表单提供的验证码图片响应对象,再通过io库的BytesIO函数将该对象内容转换为二进制数据,最后使用PIL
模块的Image.open函数打开验证码,如图6
所示。
图6 验证码示例
对于人类而言,能够十分容易地认出图中字符,但计算机却很难直接理解这种图像。故而,为了使图片文字更易于被机器识别,需先尽可能滤除图中的干扰信息,最终达到“白底黑字”的效果。由于图中的文字均是黑而干扰线不是,因此可以利用point方法为图像设置阈值,将全部非黑的像素点转换成白(红、绿、蓝三值都是255),最终完成降噪的图像,如图7
所示。
图7 处理后图像
尽管图中仍有些无法根据像素点RGB值过滤的小黑点,但这些噪点对文字识别产生的实际影响较小,且能通过采取其他措施去除。
3.3 文字识别
脚本工具主要采用pytesseract模块识别图片文字。pytesseract作为一个基于Tesseract的独立封装包,除了拥有极高的精确度,也具有不错的灵活性,是目前公认最优秀的开源OCR库之一。
在实际测试中,直接从pytesseract调用image_to_string 函数识别已降噪的验证码图片,主要会产生两类错误。
(1)多识别了若干字符。如将文字间的间隙视作了空格,或是将小黑点判断为句号“.”和冒号“:”。对于这种问题,可借助标准库的replace方法或re模块(正则表达式)的sub 函数将这些多余字符从字符串中“删除”。
(2)识别错文字本身,如将字母l判断成数字1。为减少此类错误,可为Tesseract提供不同字体的样本图片,并创建相应的矩形定位文件(.box后缀)告知其正确表示的字符及每个字符的具体位置,进而训练Tesseract不断改进完善。
4 文档读取与写入
前文已详细介绍了登录各种不同的物流并从中提取页面数据的具体算法,但文例要求追踪的运单号均统一记录在本地Excel文件中,而采集到的物流信息则需要分别存放在对应行的单元格内。因此,文章接下来将继续讨论如何对文档进行读取与写入操作。
4.1 运单号读取
在读取运单号前,应先准确定位目标文档。当需要处理的文件较多时(如为了区分不同物流公司的运单,将单号分别存入若干Excel文档内),可以参考如下代码:
folderPath = “C://Users/irhqbt/AppData/Local/Programs/ Python/Python37/Program”
for file in os.listdir(folderPath):
if os.path.splitext(file)[1] ==  ‘.xlsx’:
filePath = os.path.join(folderPath, file)
trackingNumber = Read(filePath)
该程序先指明文档所在目录路径,再借助os模块查文件夹下的全部.xlsx格式文件,并将符合要求的文件名称与目录路径拼接成文档的绝对路径。最后,调用自定义的Read 函数即可访问这些表格。
Read函数核心代码如下:
table = pd.ad_excel((path), sheet_ name=’Tracking Number’, usecols=[0]))
number = ‘’
for i in range(0, len(table)):
number = number + table.loc[i].values[0] + ‘‘
return number
该函数首先通过pandas库的read_excel函数读取Excel 文档的Tracking Number表单的第一列(即运单号所在位置),再调用DataFrame函数将获取的表格数据转换为数据框结构以便进行后续数据处理工作。鉴于有些物流支持同时查询多个运单状态,脚本工具也直接将数据框中的各个运单号(由空格隔开)合成一个字符串,从而减少网页访问次数。最后,Read函数将采集的运单号返回给主函数,程序便能继续执行第2、3节的页面访问和表单提交操作。
4.2 物流信息写入
当网络爬虫成功获取各个运单的物流信息后,程序将进入另一个自定义函数Write展开写入工作。因为无论是由text方法导出的文本还是json方法加载的数据都是字符串格式,所以可以通过find、rfind等方法或正则表达式从中抓取关键信息。又由于“写入”的本质是“覆盖”而不是“修改”原文档,因此必须将采集到的每个运单状态与对应运单号作为单独元素重新放入“列表”中,再创建一个字典将这两个列表分别作为一
个“键”匹配的“值”。
除了读取文件时获得的表格数据外,DataFrame函数同样能将字典数据转换为数据框格式。最后,调用数据框对象的to_excel方法即可“新建”一个Excel文件并将物流信息写入其中。
5 结 语
本文详细介绍了如何基于Python编程语言开发一个能自动追踪物流信息的网络爬虫,依次完成读取运单号、访问查询页面、写入物流信息三步操作。其中,文档读写功能的实现方式相近,主要应用了os与pandas模块;而采集网页数据的过程则较为复杂——选用requests模块作为向网站发送请求的核心库,但鉴于某些网站必须在提交符合要求的表单后才能顺利登录,需要再引入io、PIL、pytesseract等库以正确识别验证码图片。笔者以追踪物流信息为例,介绍了如何借助Python从不同类型网页采集数据及对本地文件执行读写操作,希望能为类似的功能开发与技术研究提供借鉴。
参考文献
[1][美]Bill Lubanovic.Python语言及其应用[M].丁嘉瑞,梁杰,禹常隆,译.北京:人民邮电出版社,2016.
[2][美]Ryan Mitchell. Python网络数据采集[M].陶俊杰,陈小莉,译.北京:人民邮电出版社,2016.
[3][德]Katharine Jarmul,[澳]Richard Lawson.用Python 写网络爬虫[M].李斌,译.第2版.北京:人民邮电出版社,2018.