Python网络爬虫原理及实践 | 京东云技术团队

作者:京东物流 田禹

1 网络爬虫

网络爬虫:是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。

网络爬虫相关技术和框架繁多,针对场景的不同可以选择不同的网络爬虫技术。

2 Scrapy框架(Python)

2.1. Scrapy架构

2.1.1. 系统架构

2.1.2. 执行流程

总结爬虫开发过程,简化爬虫执行流程如下图所示:

爬虫运行主要流程如下:

(1) Scrapy启动Spider后加载Spaider的start_url,生成request对象;

(2) 经过middleware完善request对象(添加IP代理、User-Agent);

(3) Downloader对象按照request对象下载页面;

(4) 将response结果传递给spider的parser方法解析;

(5) spider获取数据封装为item对象传递给pipline,解析的request对象将返回调度器进行新一轮的数据抓取;

2.2. 框架核心文件介绍

2.2.1. scrapy.cfg

scrapy.cfg是scrapy框架的入口文件,settings节点指定爬虫的配置信息,deploy节点用于指定scrapyd服务的部署路径。

|
[settings]

default = sfCrawler.settings

[deploy]

url =http://localhost:6800/

project = jdCrawler

|

2.2.2. settings.py

settings主要用于配置爬虫启动信息,包括:并发线程数量、使用的middleware、items等信息;也可以作为系统中的全局的配置文件使用。

**注:**目前主要增加了redis、数据库连接等相关配置信息。

2.2.3. middlewares.py

middleware定义了多种接口,分别在爬虫加载、输入、输出、请求、请求异常等情况进行调用。

**注:**目前主要用户是为爬虫增加User-Agent信息和IP代理信息等。

2.2.4. pipelines.py

用于定义处理数据的Pipline对象,scrapy框架可以在settings.py文件中配置多个pipline对象,处理数据的个过程将按照settings.py配置的优先级的顺序顺次执行。

**注:**系统中产生的每个item对象,将经过settings.py配置的所有pipline对象。

2.2.5. items.py

用于定义不同种数据类型的数据字典,每个属性都是Field类型;

2.2.6. spider目录

用于存放Spider子类定义,scrapy启动爬虫过程中将按照spider类中name属性进行加载和调用。

2.3. 爬虫功能扩展说明

2.3.1. user_agents_middleware.py

通过procces_request方法,为request对象添加hearder信息,随机模拟多种浏览器的User-Agent信息进行网络请求。

2.3.2. proxy_server.py

通过procces_request方法,为reques对象添加网络代理信息,随机模拟多IP调用。

2.3.3. db_connetion_pool.py

文件位置
db_manager/db_connetion_pool.py,文件定义了基础的数据连接池,方便系统各环节操作数据库。

2.3.4. redis_connention_pool.py

文件位置db_manager/ redis_connention_pool.py,文件定义了基础的Redis连接池,方便系统各环节操作Redis缓存。

2.3.5. scrapy_redis包

scrapy_redis包是对scrapy框架的扩展,采用Redis作为请求队列,存储爬虫任务信息。

spiders.py文件:定义分布式RedisSpider类,通过覆盖Spider类start_requests()方法的方式,从Redis缓存中获取初始请求列表信息。其中RedisSpider子类需要为redis_key赋值。

pipelines.py文件:定义了一种简单的数据存储方式,可以直接将item对象序列化后保存到Redis缓存中。

dupefilter.py文件:定义数据去重类,采用Redis缓存的方式,已经保存的数据将添加到过滤队列中。

queue.py文件:定义几种不同的入队和出队顺序的队列,队列采用Redis存储。

2.4. 微博爬虫开发示例

2.4.1. 查找爬虫入口

2.4.1.1. 站点分析

网站一般会分为Web端和M端两种,两种站点在设计和架构上会有较大的差别。通常情况下Web端会比较成熟,User-Agent检查、强制Cookie、登录跳转等限制,抓取难度相对较大,返回结果以HTML内容为主;M端站点通常采用前后端分离设计,大多提供独立的数据接口。所以站点分析过程中优先查找M端站点入口。微博Web端及M端效果如图所示:

微博Web端地址:https://weibo.com/,页面显示效果如下图所示:

注:图片来源于微博PC端截图

微博M端地址:https://m.weibo.cn/?jumpfrom=weibocom,页面显示效果如下图所示:

注:图片来源于微博M端截图

2.4.1.2. HTML源码分析

Web端站点和M端站点返回结果都是HTML格式,部分站点为了提升页面渲染速度,或者为了增加代码分析难度,通过动态JavaScrip执行等方式,动态生成HTML页面,网络爬虫缺少JS执行和渲染过程,很难获取真实的数据,微博Web端站点HTML代码片段如下所示:

脚本中的正文内容:

M端站点HTML内容:

M端HTML内容中并未出现页面中的关键信息,可以判定为前后端分离的设计方式,通过Chrome浏览器开发模式,能够查看所有请求信息,通过请求的类型和返回结果,基本可以确定接口地址,查找过程如下图所示:

注:图片来源于微博M端截图

(1) 打开Chrome开发者工具,刷新当前页面;

(2) 修改请求类型为XHR,筛选Ajax请求;

(3) 查看所有请求信息,忽略没有返回结果的接口;

(4) 在接口返回结果中查找页面中相关内容。

2.4.1.3. 接口分析

接口分析主要包括:请求地址分析、请求方式、参数列表、返回结果等。

请求地址、请求方式和参数列表可以根据Chrome开发人员工具中的网络请求Header信息获取,请求信息如下图所示:

上图中接口地址采用的是GET方式请求,请求地址是unicode编码,参数内容可以查看Query String Parameters列表查看请求参数,效果如下图所示:

请求结果分析主要分析数据结构的特点,查找与正文内容相同的数据结构,同时要检查所有结果是否与正文内容一致,避免特殊返回结果影响数据解析过程。

2.4.1.4. 接口验证

接口验证一般需要两个步骤:

(1)用浏览器(最好是新开浏览器,如Chrome的隐身模式)模拟请求过程,在地址栏中输入带有参数的请求地址查看返回结果。

(2)采用Postman等工具模拟浏览器请求过程,主要模拟非Get方式的网络请求,同样也可以验证站点是否强制使用Cookie和User-Agent信息等。

2.4.2. 定义数据结构

爬虫数据结构定义主要结合业务需求和数据抓取的结果进行设计,微博数据主要用户国内的舆情系统,所以在开发过程中将相关站点的数据统一定义为OpinionItem类型,在不同站点的数据保存过程中,按照OpinionItem数据结构的特点装配数据。在items.py文件中定义舆情数据结构如下所示:

class OpinionItem(Item):
    rid = Field()
    pid = Field()
    response_content = Field()  # 接口返回的全部信息
    published_at = Field()  # 发布时间
    title = Field()  # 标题
    description = Field()  # 描述
    thumbnail_url = Field()  # 缩略图
    channel_title = Field()  # 频道名称
    viewCount = Field()  # 观看数
    repostsCount = Field()  # 转发数
    likeCount = Field()  # 点赞数
    dislikeCount = Field()  # 不喜欢数量
    commentCount = Field()  # 评论数
    linked_url = Field()  # 链接
    updateTime = Field()  # 更新时间
    author = Field()  # 作者
    channelId = Field()  # 渠道ID
    mediaType = Field()  # 媒体类型
    crawl_time = Field() # 抓取时间
    type = Field() # 信息类型:1 主贴 2 主贴的评论

2.4.3. 爬虫开发

微博爬虫采用分布式RedisSpider作为父类,爬虫定义如下所示:

class weibo_list(RedisSpider):
    name = 'weibo'
    allowed_domains = ['weibo.cn']
    redis_key = 'spider:weibo:list:start_urls'
 
    def parse(self, response):
        a = json.loads(response.body)
        b = a['data']['cards']
        for j in range(len(b)):
            bb = b[j]
            try:
                for c in bb['card_group']:
                    try:
                        d = c['mblog']
                        link = 'https://m.weibo.cn/api/comments/show?id={}'.format(d['mid'])
                        # yield scrapy.Request(url=link, callback=self.parse_detail)
                        # 内容解析代码片段
                        opinion['mediaType'] = 'weibo'
                        opinion['type'] = '1'
                        opinion['crawl_time'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                        yield opinion
                    except Exception as e:
                        print(e)
                        continue
            except Exception as e:
                print(e)
                continue

代码解析:

代码行1:定义weibo_list类,继承RedisSpider类;

代码行2:定义爬虫名称,爬虫启动时使用;

代码行3:增加允许访问的地址域名列表;

代码行4:定义微博开始请求地址的redis key;

代码行6:定义爬虫的解析方法,爬虫下载页面后默认调用parser方法;

代码行7~21:将下载结果内容解析后装配到item对象中;

代码行22:通过yield关键字,生成Python特有的生成器对象,调用者可以通过生成器对象遍历所有结果。

2.4.4. 数据存储

数据存储主要通过定义Pipline实现类,将爬虫解析的数据进行保存。微博数据需要增加进行情感分析的加工过程,开发过程中首先将微博数据保存到Redis服务中,通过后续的情感分析解析服务获取、加工,并保存到数据库中。数据保存代码如下所示:

class ShunfengPipeline(object):
    def process_item(self, item, spider):
        if isinstance(item, OpinionItem):
            try:
                print('===========Weibo查询结果============')
                key = 'spider:opinion:data'
                dupe = 'spider:opinion:dupefilter'
 
                attr_list = []
                for k, v in item.items():
                    if isinstance(v, str):
                        v = v.replace('\'', '\\\'')
 
                    attr_list.append("%s:'%s'" % (k, v))
                data = ",".join(attr_list)
                data = "{%s}" % data
 
                # 按照数据来源、类型和唯一id作为去重标识
                single_key = ''.join([item['mediaType'], item['type'], item['rid']])
                if ReidsPool().rconn.execute_command('SADD', dupe, single_key) != 0:
                    ReidsPool().rconn.execute_command('RPUSH', key, data)
            except Exception as e:
                print(e)
                pass
 
        return item

关键代码说明:

代码行1:定义Pipline类;

代码行2:定义process_item方法,用于接收数据;

代码行3:按照Item类型分别处理;

代码行4~17:Item对象拼装为JSON字符串;

代码行18~21:对数据进行去重校验,并将数据保存到redis队列中;

代码行26:返回item对象供其他Pipline操作;

Pipline定义完成后,需要在工程中的settings.py文件中进行配置,配置内容如下所示:

|
# 配置项目管道

ITEM_PIPELINES = {

“sfCrawler.pipelines.JdcrawlerPipeline”: 401,

“sfCrawler.pipelines_manage.mysql_pipelines.MySqlPipeline”: 402,

“sfCrawler.pipelines_manage.shunfeng_pipelines.ShunfengPipeline”: 403,

}

|

爬虫入门地址:
https://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/tutorial.html

3 WebMagic框架(Java)

3.1 前言

总结Scrapy使用过程中存在的问题,以及爬虫系统后期上线需求考虑,采用Java语言进行爬虫的设计与开发。具体原因如下:

(1)上线基础环境依赖:需要使用线上Clover、JimDB、MySQL等基础环境;

(2)可扩展性强:以现有框架为基础,二次封装Request请求对象,实现通用网络爬虫开发,提供易扩展的地址生成、网页解析接口。

(3)集中部署:通过部署通用爬虫方式,抓取所有支持站点的爬虫,解决Scrapy框架一站点一部署问题。

(4)反-反爬虫:部分站点针对Scrapy框架请求特点,实施反爬虫策略(如:天猫),拒绝所有爬虫请求。WebMagic模拟浏览器请求,不受该爬虫限制。

3.2 WebMagic概述

(内容来源:https://webmagic.io/docs/zh/posts/ch1-overview/architecture.html)

3.2.1 总体架构

WebMagic的结构分为Downloader、PageProcessor、Scheduler、Pipeline四大组件,并由Spider将它们彼此组织起来。这四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。WebMagic的设计参考了Scapy,但是实现方式更Java化一些。

而Spider则将这几个组件组织起来,让它们可以互相交互,流程化的执行,可以认为Spider是一个大的容器,它也是WebMagic逻辑的核心。

WebMagic总体架构图如下:

3.1.2 WebMagic的四个组件

3.1.2.1 Downloader

Downloader负责从互联网上下载页面,以便后续处理。WebMagic默认使用了Apache HttpClient作为下载工具。

3.1.2.2 PageProcessor

PageProcessor负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用Jsoup作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup。

在这四个组件中,PageProcessor对于每个站点每个页面都不一样,是需要使用者定制的部分。

3.1.2.3 Scheduler

Scheduler负责管理待抓取的URL,以及一些去重的工作。WebMagic默认提供了JDK的内存队列来管理URL,并用集合来进行去重。也支持使用Redis进行分布式管理。

除非项目有一些特殊的分布式需求,否则无需自己定制Scheduler。

3.1.2.4 Pipeline

Pipeline负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic默认提供了“输出到控制台”和“保存到文件”两种结果处理方案。

Pipeline定义了结果保存的方式,如果你要保存到指定数据库,则需要编写对应的Pipeline。对于一类需求一般只需编写一个Pipeline。

3.1.3 用于数据流转的对象

3.1.3.1 Request

Request是对URL地址的一层封装,一个Request对应一个URL地址。

它是PageProcessor与Downloader交互的载体,也是PageProcessor控制Downloader唯一方式。

除了URL本身外,它还包含一个Key-Value结构的字段extra。你可以在extra中保存一些特殊的属性,然后在其他地方读取,以完成不同的功能。例如附加上一个页面的一些信息等。

3.1.3.2 Page

Page代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。

Page是WebMagic抽取过程的核心对象,它提供一些方法可供抽取、结果保存等。在第四章的例子中,我们会详细介绍它的使用。

3.1.3.3 ResultItems

ResultItems相当于一个Map,它保存PageProcessor处理的结果,供Pipeline使用。它的API与Map很类似,值得注意的是它有一个字段skip,若设置为true,则不应被Pipeline处理。

3.1.4 控制爬虫运转的引擎–Spider

Spider是WebMagic内部流程的核心。Downloader、PageProcessor、Scheduler、Pipeline都是Spider的一个属性,这些属性是可以自由设置的,通过设置这个属性可以实现不同的功能。Spider也是WebMagic操作的入口,它封装了爬虫的创建、启动、停止、多线程等功能。下面是一个设置各个组件,并且设置多线程和启动的例子。详细的Spider设置请看第四章——爬虫的配置、启动和终止。

public static void main(String[] args) {
    Spider.create(new GithubRepoPageProcessor())
            //从https://github.com/code4craft开始抓   
            .addUrl("https://github.com/code4craft")
            //设置Scheduler,使用Redis来管理URL队列
            .setScheduler(new RedisScheduler("localhost"))
            //设置Pipeline,将结果以json方式保存到文件
            .addPipeline(new JsonFilePipeline("D:\\data\\webmagic"))
            //开启5个线程同时执行
            .thread(5)
            //启动爬虫
            .run();
}

3.3 通用爬虫分析及设计

3.2.1 通用爬虫功能分析

(1)单个应用同时支持多个站点的数据抓取;

(2)支持集群部署;

(3)容易扩展;

(4)支持重复抓取;

(5)支持定时抓取;

(6)具备大数据分析扩展能力;

(7)降低大数据分析集成复杂度,提高代码复用性;

(8)支持线上部署;

3.2.2 通用爬虫设计

通用爬虫设计思路是在WebMagic基础上,将Scheduler、Processor、Pipeline进行定制,设计如下图所示:

设计过程中按照爬虫开发的特点,将爬虫实现过程拆分为两个环节:生成请求和内容解析。

(1)生成请求(UrlParser):按照不同站点的请求地址及参数特点(如:Get/Post请求方式,URL参数拼接等)及业务需要(如:使用国内代理还是国外代理),按照站点参数拼装成为通用的Request请求对象,指导Downloader进行网页下载。

(2)内容解析(HtmlParser):按照解析网页内容的特点,通过XPATH、JSON等方式对页面内容进行提取过程,每个内容解析器只针对相同页面内容进行解析,当内容中包含深度页面抓取的请求时,由UrlParser生成新的请求对象并返回给调度器。

3.2.3 任务调度设计

为了实现爬虫的分布式,将Scheduler功能进行弱化,实现过程增加Clover、Worker、Redis环节。Clover负责定时调度Worker生成默认请求对象(一般为检索功能、首页重复抓取任务等),将生成的请求对象添加到Redis队列中,Scheduler只负责从Redis队列中获取请求地址即可。

3.2.4 Processor设计

Processor用于解析Downloader下载的网页内容,为了充分利用服务器网络和计算资源,设计之初考虑能够将网页下载和内容解析拆分到不同服务中进行处理,避免爬虫节点CPU时间过长造成网络带宽的浪费。所以,设计过程中按照爬虫内部解析和外部平台集成解析两种方式进行设计。

(1)爬虫内部解析:即下载内容直接由Processor完成页面解析过程,生成Items对象和深度Request对象。为了简化对多个站点内容的解析过程,Processor主要负责数据结构的组织和HtmlParser的调用,通过Spring IOC的方式实现多个站点HtmlParser集成过程。

(2)外部平台集成:能够将爬虫抓取内容通过MQ等方式,实现其他平台或服务的对接过程。实现过程中可以将抓取的网页内容组织成为文本类型,通过Pipline方式将数据发送到JMQ中,按照JMQ方式实现与其他服务和平台的对接过程。其他服务可以复用HtmlParser和UrlParser完成内容解析过程。

3.2.5 Pipline设计

Pipline主要用于数据的转储,为了适用Processor的两种方案,设计MySQLPipeline和JMQPipeline两种实现方式。

3.4 通用爬虫实现

3.4.1 Request

WebMagic提供的Request类能够满足网络请求的基本需求,包括URL地址、请求方式、Cookies、Headers等信息。为了实现通用的网络请求,对现有请求对象进行业务扩展,增加是否过滤、过滤token、请求头类型(PC/APP/WAP)、代理IP国别分类、失败重试次数等。扩展内容如下:

/**
 * 站点
 */
private String site;
/**
 * 类型
 */
private String type;
 
/**
 * 是否过滤 default:TRUE
 */
private Boolean filter = Boolean.TRUE;
/**
 * 唯一token,URL地址去重使用
 */
private String token;
/**
 * 解析器名称
 */
private String htmlParserName;
 
/**
 * 是否填充Header信息
 */
private Integer headerType = HeaderTypeEnums.NONE.getValue();
 
/**
 * 国别类型,用于区分使用代理类型
 * 默认为国内
 */
private Integer nationalType = NationalityEnums.CN.getValue();
 
/**
 * 页面最大抓取深度,用于限制列表页深度钻取深度,按照访问次数依次递减
 * <p>Default: 1</p>
 * <p> depth = depth - 1</p>
 */
private Integer depth = 1;
 
/**
 * 失败重试次数
 */
private Integer failedRetryTimes;

3.4.2 UrlParser & HtmlParser

3.4.2.1 UrlParser实现

UrlParser主要用与按照参数列表生成固定的请求对象,为了简化Workder的开发过程,在接口中增加了生成初始化请求的方法。

/**
 * URL地址转换
 * @author liwanfeng1
 */
public interface UrlParser {
    /**
     * 获取定时任务初始化请求对象列表
     * @return 请求对象列表
     */
    List<SeparateRequest> getStartRequest();
 
    /**
     * 按照参数生成Request请求对象
     * @param params
     * @return
     */
    SeparateRequest parse(Map<String, Object> params);
}
3.4.2.2 HtmlParser实现

HtmlParser主要是将Downloader内容进行解析,并返回data列表及深度抓取的Request对象。实现如下:

/**
 * HTML代码转换
 * @author liwanfeng1
 */
public interface HtmlParser {
    /**
     * HTML格式化
     * @param html 抓取的网页内容
     * @param request 网络请求的Request对象
     * @return 数据解析结果
     */
    HtmlDataEntity parse(String html, SeparateRequest request);
}
  
/**
 * @author liwanfeng1
 * @param <T> 数据类型
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HtmlDataEntity<T extends Serializable> {
    private List<T> data;
    private List<SeparateRequest> requests;
 
    /**
     * 添加数据对象
     * @param obj 数据对象
     */
    public void addData(T obj){
        if(data == null) {
            data = new ArrayList<>();
        }
        data.add(obj);
    }
 
    /**
     * 添加Request对象
     * @param request 请求对象
     */
    public void addRequest(SeparateRequest request) {
        if(requests == null) {
            requests = new ArrayList<>();
        }
        requests.add(request);
    }
}

3.4.3 Worker

Workder的作用是定时生成请求对象,结合UrlParser接口的应用,设计统一的WorkerTask实现类,代码如下:

/**
 *
 * @author liwanfeng1
 */
@Slf4j
@Data
public class CommonTask extends AbstractScheduleTaskProcess<SeparateRequest> {
 
    private UrlParser urlParser;
 
    private SpiderQueue spiderQueue;
    /**
     * 获取任务列表
     * @param taskServerParam 参数列表
     * @param i 编号
     * @return 任务列表
     */
    @Override
    protected List<SeparateRequest> selectTasks(TaskServerParam taskServerParam, int i) {
        return urlParser.getStartRequest();
    }
 
    /**
     * 执行任务列表,组织Google API请求地址,添加到YouTube列表爬虫的队列中
     * @param list 任务列表
     */
    @Override
    protected void executeTasks(List<SeparateRequest> list) {
        spiderQueue.push(list);
    }
}

添加Worker配置如下:


<!-- Facebook start -->
<bean id="facebookTask" class="com.jd.npoms.worker.task.CommonTask">
    <property name="urlParser">
        <bean class="com.jd.npoms.spider.urlparser.FacebookUrlParser"/>
    </property>
    <property name="spiderQueue" ref="jimDbQueue"/>
</bean>
 
<jsf:provider id="facebookTaskProcess"
              interface="com.jd.clover.schedule.IScheduleTaskProcess" ref="facebookTask"
              server="jsf" alias="woker:facebookTask">
</jsf:provider>
<!-- Facebook end -->

3.4.4 Scheduler

调度主要用于推送、拉取最新任务,以及辅助的推送重复验证、队列长度获取等方法,接口设计如下:

/**
 * 爬虫任务队列
 * @author liwanfeng
 */
public interface SpiderQueue {
 
    /**
     * 添加队列
     * @param params 爬虫地址列表
     */
    default void push(List<SeparateRequest> params) {
        if (params == null || params.isEmpty()) {
            return;
        }
        params.forEach(this::push);
    }
 
 
    /**
     * 将SeparaterRequest地址添加到Redis队列中
     * @param separateRequest SeparaterRequest地址
     */
    void push(SeparateRequest separateRequest);
 
    /**
     * 弹出队列
     * @return SeparaterRequest对象
     */
    Request poll();
 
 
    /**
     * 检查separateRequest是否重复
     * @param separateRequest 封装的爬虫url地址
     * @return 是否重复
     */
    boolean isDuplicate(SeparateRequest separateRequest);
 
    /**
     * 默认URL地址生成Token<br/>
     * 建议不同的URLParser按照站点地址的特点,生成较短的token
     * 默认采用site、type、url地址进行下划线分割
     * @param separateRequest 封装的爬虫url地址
     */
    default String generalToken(SeparateRequest separateRequest) {
        return separateRequest.getSite() + "_" + separateRequest.getType() + "_" + separateRequest.getUrl();
    }
 
    /**
     * 获取队列总长度
     * @return 队列长度
     */
    Long getQueueLength();
}

4 浏览器调用爬虫(Python)

浏览器调用爬虫主要借助Selenium与ChromeDriver技术,通过本地化浏览器调用方式加载并解析页面内容,实现数据抓取。浏览器调用主要解决复杂站点的数据抓取,部分站点通过流程拆分、逻辑封装、代码拆分、代码混淆等方式增加代码分析的复杂度,结合请求拆分、数据加密、客户端行为分析等方式进行反爬操作,使爬虫程序无法模拟客户端请求过程向服务端发起请求。

该种方式主要应用于顺丰快递单号查询过程,订单查询采用腾讯滑动验证码插件进行人机验证。基本流程如下图所示:

首先配置ChromeDriver组件到操作系统中,组件下载地址:
https://chromedriver.storage.googleapis.com/index.html?path=2.44/,将文件保存到系统环境变量“PATH”中指定的任意路径下,建议:C:\Windows\system32目录下。

组件添加验证:启用命令行窗口->任意路径运行“ChromeDriver.exe”,程序会以服务形式运行。

爬虫实现过程如下:

(1)启动浏览器:为了实现爬虫的并发,需要通过参数方式对浏览器进行优化设置;

启动浏览器

def getChromeDriver(index):
    options = webdriver.ChromeOptions()
    options.add_argument("--incognito")  # 无痕模式
    options.add_argument("--disable-infobars")  # 关闭菜单栏
    options.add_argument("--reset-variation-state")  # 重置校验状态
    options.add_argument("--process-per-tab")  # 独立每个tab页为单独进程
    options.add_argument("--disable-plugins")  # 禁用所有插件
    options.add_argument("headless")  # 隐藏窗口
    proxy = getProxy()
    if proxy is not None:
        options.add_argument("--proxy-server==http://%s:%s" % (proxy["host"], proxy["port"]))  # 添加代理
 
    return webdriver.Chrome(chrome_options=options)

(2)加载页面:调用浏览器访问指定地址页面,并等待页面加载完成;

driver.get('http://www.sf-express.com/cn/sc/dynamic_function/waybill/#search/bill-number/' + bill_number)
driver.implicitly_wait(20) #最长等待20秒加载时间

(3)切换Frame:验证码采用IFrame方式加载到当前页面,接下来需要对页面元素进行操作,需要将driver切换到iframe中;

driver.switch_to.frame("tcaptcha_popup")
driver.implicitly_wait(10) #等待切换完成,其中iframe加载可能有延迟

(4)滑动模块:滑动页面中的滑块到指定位置,实现验证过程;

滑动验证码操作过程如下图所示:

滑动模块执行距离在240像素前后,整个滑动过程取样14个,模拟抛物线执行过程控制滑动速度,将这个滑动过程分为20次移动(避免每次采样结果相同),分析如下图所示:

代码如下:

# 随机生成滑块拖动轨迹
def randomMouseTrace():
    trace = MouseTrace()
    trace.x = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5]
    trace.y = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    trace.time = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
    return trace
 
 
# 拖动滑块
def dragBar(driver, action):
    dragger = driver.find_element_by_id("tcaptcha_drag_button")
    action.click_and_hold(dragger).perform()  # 鼠标左键按下不放
 
    action.reset_actions()
    trace = randomMouseTrace()
    for index in range(trace.length()):
        action.move_by_offset(trace.x[index], trace.y[index]).perform()  # 移动一个位移
        action.reset_actions()
        time.sleep(trace.time[index])  # 等待停顿时间
 
    action.release().perform()  # 鼠标左键松开
    action.reset_actions()
 
    return driver.find_element_by_id("tcaptcha_note").text == ""

(4)数据解析及存储:数据解析过程主要是按照id或class进行元素定位获取文本内容,将结果插入到数据库即完成数据抓取过程。

(5)其他:采用Python的threading进行多线程调用,将订单编号保存到Redis中实现分布式任务获取过程,每执行一次将POP一个订单编号。

待改进:

(1)模块滑动速度和时间固定,可以进行随机优化;

(2)未识别滑块释放位置,目前采用滑块更新的方式重试,存在一定的错误率;

(3)如果不切换代理IP,需要对浏览器启动进行优化,减少启动次数,提升抓取速度;

5 gocolly框架(Go)

多线程并发执行是Go语言的优势之一,Go语言通过“协程”实现并发操作,当执行的协程发生I/O阻塞时,会由专门协程进行阻塞任务的管理,对服务器资源依赖更少,抓取效率也会有所提高。

(以下内容转自:https://www.jianshu.com/p/23d4ecb8428f)

5.1 概述

gocolly是用go实现的网络爬虫框架gocolly快速优雅,在单核上每秒可以发起1K以上请求;以回调函数的形式提供了一组接口,可以实现任意类型的爬虫;依赖goquery库可以像jquery一样选择web元素。

gocolly的官方网站是http://go-colly.org/,提供了详细的文档和示例代码。

5.2 安装配置

安装

go get -u github.com/gocolly/colly/

引入包

import "github.com/gocolly/colly"

5.3 流程说明

5.3.1 使用流程

使用流程主要是说明使用colly抓取数据前的准备工作

  • 初始化Collector对象, Collector对象是colly的全局句柄

  • 设置全局设置,全局设置主要是设置colly 句柄的代理设置等

  • 注册抓取回调函数, 主要是用于在抓取数据后在数据处理的各个流程提取数据以及出发其他操作

  • 设置辅助工具,如抓取链接的存放队列,数据清洗队列等

  • 注册抓取链接

  • 启动程序开始抓取

5.3.2 抓取流程

每次抓取数据流程中的各个节点都会尝试触发用户注册的抓取回调函数,以完成提取数据等需求, 抓取流程如下。

  • 根据链接每次准备抓取数据前调用 注册的OnRequest做每次抓取前的预处理工作

  • 当抓取数据失败时会调用OnError做错误处理

  • 抓取到数据后调用OnResponse,做刚抓到数据时的处理工作

  • 然后分析抓取到的数据会根据页面上的dom节点触发OnHTML回调进行数据分析

  • 数据分析完毕后会调用OnScraped函数进行每次抓取后的收尾工作

5.4. 辅助接口

colly也提供了部分辅助接口,协助完成数据抓取分析流程, 以下列举一部分主要的支持。

  • queue 用于存放等待抓取的链接

  • proxy 用于代理发起抓取源

  • thread 支持多携程并发处理

  • filter 支持对特殊链接进行过滤

  • depth 可以设置抓取深度控制抓取

5.5. 实例

更多可以参考源码链接中的例子(https://github.com/gocolly/colly/tree/master/_examples)

相关推荐

AI 会写代码之后,交付为什么还没变快?

AI 写代码,只是这场变革的起点,而不是终点。真正决定一个组织能否吃到 AI 红利的,不是"能不能用 AI 写出代码",而是敢不敢用 AI 重新设计交付流程、重新划分角色分工、重新组织团队结构。研发视角能把工具和个人推到极致,却会在组织的边界前止步;只有站上组织视角,把AI × 工具一路推进到AI × 组织,那些被 AI 照出来的摩擦,才会真正被抹平。国际技术部 Multi AI Native 想做的,正是这件事——让 Agent 成为你可靠的数字战友,让每一次迭代交付都快一个量级;

京东科技开发者 246

OxygenData-Vibe搭建:用 AI Coding重新定义数据看板搭建

长期以来,数据看板开发主要有两种方式:一种是使用标准化 BI 工具,交付快、成本低,但图表样式、交互体验和功能扩展都受限于 BI 本身的能力;另一种是自研定制开发,能够充分满足复杂和个性化需求,但需要投入大量研发资源,交付周期也更长。比如一个通过 BI 只需要 1 天就能搭建出来的看板,如果采用自研方式,可能需要投入 30 PD 的研发人力。既然 BI 交付这么快,为什么不把所有看板需求都交给 BI?原因在于,业务某些场景需要的不只是把数据展示出来。

京东科技开发者 309

当我谈数据库高可用时我谈些什么

大家都经常听到说数据库的双1,那双1指的是什么呢?参数=1(最安全)≠1(更快但可能丢)每次提交都把 redo 刷盘0/2:攒着刷,崩溃丢最近 1 秒每次提交都把 binlog 刷盘0/N:攒着刷,崩溃丢最近若干条"双 1"(都设为 1)= 单机不丢已提交事务的前提。生产核心库必须双 1;代价是每次提交两次 fsync。小结:单机层用 WAL 提速、用 2PC 保证 redo/binlog 一致、用崩溃恢复裁决半成品事务、用双 1 兜底持久性。

京东科技开发者 271

ScoAI:面向供应链的运筹原生决策智能

ScoAI 关注的并非 AI 能否进入供应链,而是 AI 参与供应链决策之后,哪些内容适合通过数据学习,哪些关系必须显式计算,以及哪些结果需要依据执行记录进行验证。其基本分工是:供应链业务定义问题、资源与行动后果,运筹学表达目标、约束和协同关系,AI 提供可复用的估计与判断。真正用于改进后续决策的,不仅是模型生成的初始结果,还包括实际采取的行动及其产生的业务反馈。

京东科技开发者 367

拆解海博 AI-Native 落地保障:海博团队 AI 知识库能力建设

绕了一大圈,回头看,我们其实一直在解开头那三个结:AI 读不懂系统、影响面靠人脑记、测试预期没处查。做到这里,本质上是把"AI 用不好"这个问题,从"是不是该换个更强的模型"重新定义成了"怎么把上下文经营好"。顺着这个思路,我们攒下了这么几样东西:•。

京东科技开发者 518

从零构建一个生产级记忆型 AI Agent —— AgentScope 项目全景技术与学习指南

domain 层端口接口(domain)职责适配器实现(infra)对话流水账读写经验记忆检索/落库请求级幂等四态会话元数据 CRUD会话级 Skill 选择态Skill 元数据HITL 权限网关(domain 内含逻辑,见第四篇)StreamSink技术中立的流式下沉(client 层桥接)以幂等端口为例,头部注释明确写道:实现 domain 端口,持有实体不出 infra 边界,对外只暴露嵌套视图GateResult。

京东科技开发者 571

数据涅槃:Agentic 重塑搜推数据生产体系

当前大模型已经能够理解需求、生成代码和解释错误,但数据生产的难点并不只是“写出一段 SQL”。一个需求还要经过数据源确认、口径澄清、加工编排、资源规划、任务提交、调度运行、结果交付和故障处置。任何一步缺少都可能让“看起来正确”的任务变成生产事故。

京东科技开发者 337

81.8 秒的视频,我们改了 15 个版本:一次纯 Codex 驱动的 AI-native 视频实践

81.8 秒、15 个版本,这些数字真正想说明什么?视频制作正在从一组专业软件里的复杂操作,变成一场围绕目标、参考和反馈展开的持续对话。AI 已经可以接住从脚本到交付的大量工作。你可以把精力留给更重要的部分:理解观众、选择故事、判断好坏。过去停在 PPT 和脑海里的想法,现在有机会被快速做成一支能看、能改、也愿意转发的视频。如果明天就要介绍你手头的项目,你最想先把哪一步交给 AI?

京东科技开发者 433

Codex 驱动的移动端AI全栈开发:从 Relay 原型图到可交付链路

AI 编程工具降低了跨端开发门槛,但“让 AI 写页面”并不等于“完成移动端交付”。PC 运营端页面通常以功能闭环为中心,设计约束较弱,研发可以基于组件库快速搭建;移动端 C 端页面直接面对用户,Relay 原型图和 UI 设计稿不仅是视觉参考,更是交互、信息层级、状态反馈和业务信任感的综合契约。

京东科技开发者 566

Claude Code vs Codex 记忆体系深度对比

Claude Code —— 分层自动记忆系统四层分层架构(CLAUDE.md / Auto Memory / Session Memory / AutoDream)构成自优化闭环,系统主动捕获、分类、去冗并跨会话接力经验。走的是自动化优先路线——让机器替人记忆、替人整理,以少量可控性的不确定换取强大的经验积累能力。Codex —— 声明式契约系统两层扁平架构(AGENTS.md 静态声明层 + 会话上下文层),记忆完全由人显式订立、显式修改,系统只读契约、绝不擅自变更。走的是可控性优先。

京东科技开发者 605

上游给空、下游拿到 -1,我把故障一直追到了 commons-beanutils 的构造函数

这篇手记是一份事后复盘,写在我们已经把修复推上线、把测试用例跑绿、把约束文档沉淀进团队库之后。非研发同事,可以直接从第八章开始看,前七章都是讲排障和代码分析。故障本身不复杂,修复也不复杂,但这次排查真正让我想写下来的,不是技术本身——而是从问题提报到解决全程AI的作用:问题定位:业务方反馈了一个异常单号说「下发失败」,一线运维同事直接在日志系统捞到相关的系统日志,用IDE打开下游代码库,把异常信息给到JoyCode——AI 直接指向了问题是serialType值非法,系统只接受0和1,而当前上游给的是-1。

京东科技开发者 703

SKill测评:谁才是真正的Skill之王?

巧合的是,阿里近期开源了一款名为 Skill-Up 的测评工具——它专门面向 Agent Skill 开发者,支持在 Claude Code、Codex、Qoder CLI 等真实 Agent Engine 环境中验证 Skill 的功能正确性,并可将失败用例转化为精准的修复指引,同时支持本地与 CI 环境的持续回归。2026年,Skill 的热度不减,已成为 AI 辅助编程领域的标配。在日常研发中,无论是沉淀个人经验,还是将团队的优秀范本标准化,将知识“蒸馏”为 Skill 已是大家的常规操作。

京东科技开发者 618

让AI真正参与工作!JoyDesk已上线京东云

JoyDesk提供覆盖AI资产管理、权限控制、安全策略、审批审计、运营监控等企业级治理能力,支持按组织、角色和项目进行精细化权限管理,并支持私有化部署,确保企业数据边界清晰、AI行为全程可追溯、安全策略灵活可配置,满足金融、政企等行业对数据安全和合规治理的要求,让AI真正成为企业可信赖的生产力。JoyDesk支持MCP协议、标准API、技能等多种开放接入方式,可快速连接企业现有业务系统,将业务能力封装为AI可调用的技能和专家,让AI能够理解业务流程、调用业务系统、完成业务任务,而无需改变企业原有IT架构。

京东科技开发者 652

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手【最终版】

这个项目展示了AI Agent 在垂直内容创作领域端到端自动化:从内容理解到播客输出,全流程 AI 驱动多模态融合:文字、图片、音频、视频统一处理专业级音频质量:音色设计 + 智能混音,输出广播级品质低门槛使用:可视化配置 + 自然语言交互,无需技术背景。

京东科技开发者 556

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(高级篇)

这是一个 AI 驱动的播客创作助手,专注于从文字/视频/音频内容识别到播客制作的全流程。你可以用自然语言与它对话,也可以上传视频和音频,它会理解意图、调用相应的语音合成工具,把他们变成真实的播客音频。

京东科技开发者 720

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(进阶篇)

这是一个AI 驱动的音频内容创作助手。你可以用自然语言与它对话,它会理解意图、调用相应的语音合成工具,把文字变成真实的播客音频,并保存在本地。关于项目的详细介绍见第一篇文章:第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇)这篇文章更多介绍进阶篇添加的功能。

京东科技开发者 545

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇)

这个项目让我第一次把前端、后端、AI Agent 三块内容独立串联起来。技术选型上每一块都是当下主流的方案,实际跑通整条链路之后对"全栈"有了更具体的感受。框架版本要锁定:LangChain 这类快速迭代的框架,版本差异带来的问题远比你想象的多先跑通链路,再追求完美:新手阶段能把用户输入 → AI 处理 → 结果展示这条链跑通,比深入某个技术点更有价值多看官方文档:相比博客和教程,官方文档更新及时,特别是 LangGraph 这种新框架后续计划继续迭代,欢迎交流!

京东科技开发者 602

ICLR 2026 | 面向序列决策的贝叶斯集成方法

在这篇工作中,我们关注的是。

京东科技开发者 294

ICML 2026|NaviAgent:面向 Oxygen 智能体的可扩展工具编排

大型语言模型(LLM)越来越多地作为函数调用代理,调用外部工具来处理超出其静态知识范围的任务。然而,它们通常一次只调用一个工具,缺乏对任务结构的全局视图。由于工具之间往往相互依赖,这会导致错误累积和可扩展性差,尤其是在扩展到数百或数千个工具时。为了解决这些局限性,我们提出了NaviAgent,一种显式的双层架构,它通过基于图的工具关系建模将任务规划与工具执行解耦。在规划层,基于 LLM 的代理决定是直接响应、澄清意图,还是检索并执行一个独立于工具间复杂性的工具链。

京东科技开发者 383

实测 9 款 AI 架构图工具:从 Mermaid 美化到 GPT-Image2,一份选型清单

支持 5 类图(架构图、工作流图、时序图、数据流图、生命周期/状态机图),主题 4 种(Classic、Signal Flow、Blueprint、Editorial)+ 3 种色彩模式(Dark / Light / System Auto),输出格式含 HTML、PNG、SVG、WebM、Share Card PNG、JSON IR。它把提示词图库、图像生成案例、参考图编辑流程和命令行工具整合在一起,让 AI Agent 能直接调用 GPT Image 2 模型完成文生图、图生图、参考图编辑等任务。

京东科技开发者 895

别再守着 Claude Code 了——学会指挥它自主干活

回到开头那句:别再一句一句地喂它、然后守着屏幕。真正的用法是——把一件事想清楚、划好边界、给它一个能自我验证的目标,然后交出去。你会发现,省下来的时间不是一点半点。把重复的判断和执行还给机器,把真正需要你的判断留给自己。

京东科技开发者 584

CVPR 2026|Oxygen XR 团队在原生鱼眼高斯泼溅技术上带来新突破

京东零售技术团队联合清华大学提出DirectFisheye-GS框架,创新性地将鱼眼相机模型原生嵌入3D高斯泼溅管线,通过跨视角联合优化策略直接处理鱼眼图像,避免了传统去畸变预处理导致的信息损失。该方案在多个公开数据集上达到SOTA性能,有效解决了单视角优化引起的几何畸变和视角不一致问题,同时保持与现有3DGS工具链的兼容性。技术已应用于电商3D重建和4D体积视频等场景,显著提升了沉浸式体验。相关论文被CVPR 2026接收,为宽视场场景重建提供了高效解决方案。

京东科技开发者 353

拆解海博 AI-Native 落地保障:Harness、双 Loop、知识库与技能自主迭代实践

海博研发RoadMap 的落地,彻底终结了 AI 编程“对话式玄学”的旧模式,真正落地了AI Native 研发工程化。它证明 AI 规模化赋能研发的核心,不在于堆砌大模型能力,而在于用工程思维重构 AI 的工作范式:用结构化流程替代随机对话,用固化规则约束模型行为,用持续沉淀实现能力复利,用分层架构支撑规模化落地。

京东科技开发者 633

让 AI 快速「读懂」你的代码仓:Joy-Code-Graph 云端图谱服务的三次进化

代码知识图谱不是要取代 AI 的智能,而是要补齐它对代码全局关系的认知盲区。当 AI 能一眼看清「谁调用了谁、改动会波及哪里」,它写出的代码才真正靠谱;当这份认知安全地留在自己手里、随时被 Joygen 调用、并且永久沉淀在代码仓里——AI 编程的体验,才算真正上了一个台阶。这,就是我们做 Joy-Code-Graph 云端服务的初衷。能力共享,数据自主;一次建图,长期受用。

京东科技开发者 751

【Coding生态】从代码托管到 AI 能力底座:与Coding一起共建 AI 研发生态

前两种框架能够很好地支撑对话式能力和预设流程式能力。它们适合基于提示词或预设工作流完成研发问答、知识查询、流程编排等任务。但在更复杂的研发场景中,仅仅“回答问题”还不够。当用户业务逻辑复杂时,一旦场景进入真实落地阶段,例如下载代码、修改文件、执行命令、安装依赖、运行测试、读取完整代码文件,或者与自有系统联动、将结果渲染到 Coding 页面、推送到京 ME,AI 应用需要的不只是知识能力,还需要真实的代码库操作能力和独立可执行环境。

京东科技开发者 768

GPT-5.6、ChatGPT Work 与 Codex 更新食用指南

三档模型当前都提供 1,050,000 tokens 上下文、922,000 tokens 最大输入和 128,000 tokens 最大输出,接受文本与图像输入、输出文本,并支持 web search、file search、Computer Use、MCP、Tool Search、Skills、Apply Patch 等工具。Work 可以读取你提供的文件,通过插件连接 Slack、Google Drive、SharePoint、邮箱、日历、CRM 或项目系统,调用获批工具,并在执行中展示进度。

京东科技开发者 860

把业务流程沉淀成高质量 Skill 的实践路径

使用Record & Replay创建的Skill,尽管创建流程简单,但它的本质实际是让 AI 去操作页面,这不但有可能影响我们对电脑的操作,还有可能无法稳定复现。创建Skill时,可以使用 Codex 的 Record & Replay 插件,把一次手工操作录下来,再让 AI 生成可复用流程。刚开始切换时,很容易遇到接口报错,可以让Codex排查原因进行优化,直到可以稳定执行;补充相应业务背景,说明这件事为什么要做,谁会用,输入从哪来,输出给谁看。电脑休眠、关机、软件没开时,本地任务可能无法执行。

京东科技开发者 835

政企前台AI-Agent的架构实践:打造可复用的 AI 服务平台-【灯塔计划】

不止是爆火的龙虾,还是各种Skills,MCP、Agent,脱离的具体的场景谈方案,最后只会是一场空。这一套可复用的 AI 服务平台,截止目前,通过前后端对接规范文档,多项能力已快速接入到各个场域的不同端平台,大幅降低研发成本, 并且各业务线新场景的接入从原来的 “开发驱动” 转为 “配置驱动”,接入周期从天维度缩短至小时级。

京东科技开发者 860

京东 Oxygen xLLM 大模型推理引擎正式捐赠开放原子开源基金会,共建国产 AI Infra 生态

下一阶段 AI 基础设施的核心命题,不是简单的算力堆叠,而是让工程本身变得智能——让调度系统能自主感知负载特征并动态优化,让推理引擎能根据模型结构和硬件特性自动生成最优执行方案,让整个 AI 工程链路具备自感知、自决策、自优化的能力。未来,Oxygen xLLM 的项目官网、社区沟通渠道及微信社区将陆续向开发者开放,诚邀芯片厂商、模型厂商、云厂商、企业用户及个人开发者共同参与,让国产大模型推理引擎在开放生态中加速成长。硬件层,原生支持 GPU / NPU / MLU 多类 AI 芯片,深度适配国产芯片。

京东科技开发者 1000

【京东云】全球首个!京东全栈开源JoyAI-VL-Interaction,让大模型从“一问一答”走向“边看边说”

开源一套系统,而不只是一个模型在实时视频流中,JoyAI-VL-Interaction每秒都会做一次判断,比如,继续观察、保持沉默,发现关键事件、主动回应,遇到复杂任务,交给后台Agent处理。一个好的AI 助手,不应该一直打扰用户,而应该知道什么时候该出现,什么时候该安静,以及什么时候自己解决,什么时候交由agent解决。深耕零售、物流、健康、工业等实体产业二十余年,京东拥有全球领先的物理世界运营网络,覆盖仓储、配送、门店、直播、客服、售后海量真实场景,每天都在发生人、货、场的实时互动。

京东科技开发者 1029
上一篇: springboot升级过程中踩坑定位分析记录 | 京东云技术团队
下一篇: 京东物流常态化压测实践 | 京东云技术团队
京东云开发者
京东云开发者 企业官方账号 企业官方账号
博客等级 码龄8年 1万+粉丝 2139原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值