JB的Python之旅-豆瓣自动顶贴功能

温馨小提示

全文加上代码总6.8k个字,阅读大约10分钟,谢谢你的点击,愿能解决你的问题;

前言

前几天在小猪群里,有同学问,有人知道怎么做豆瓣自动回复功能吗?

然后群里就各种大神出马相助,各种填代码给资料的,也有同学说用selenium模拟下就好了等等,其实大家都说的对,伸手党固然不好,但是考虑到让一个不了解的同学去做这个事,的确有门槛,更别说查资料用selenium了;

豆瓣回复功能尝试

一开始的想法,也是用selenium的,但是想着,还是先模拟下,看看豆瓣的回复流程吧;

先打开需要回复的帖子,然后接着登录豆瓣,然后回到刚刚这个帖子上,观察下界面,回复按钮就在底部,点击发送就是评论了;

那我们就抓包看下请求吧,浏览器按F12,选择network,点击左边红色按钮两次,把之前的数据都清除;

接着就输入内容,点击发送按钮,然后查看network界面,不难找到发送评论的请求,从名字上看,也能确认是发送评论的请求;

然后看了下右边的请求数据,不难发现接口地址是:

而且发现,请求的时候,要带4个参数:

既然如此,那就用postman试一下吧:

请求头参数用了常规的cookie、user-agent、referer、host;

而body这块,虽然上面抓包看到有4个参数,但是实际验证只需要2个即可,发送的内容就是jbtest;

那在postman上点击send,然后在那个帖子上刷新下页面,居然能看到刚刚回复的内容;

看来,豆瓣回复功能只需要调接口就行了,都不用selenium了;

既然如此,不能写出下面这代码:

上面的代码就是定义请求头跟body参数,像豆瓣的评论接口发一个请求即可,运行下脚本,刷新下网页:

没问题,回复功能多简单,so easy;

自动功能

嗯,回复功能可以了,那Python有没有类似定时器的功能?定时执行上面的post请求就好了?

答案是有的,那就是APScheduler;

APScheduler简介

APScheduler是Python的一个定时任务框架,可以很方便的满足用户定时执行或者周期执行任务的需求, 它提供了基于日期date、固定时间间隔interval 、以及类似于Linux上的定时任务crontab类型的定时任务。

并且该框架不仅可以添加、删除定时任务,还可以将任务存储到数据库中,实现任务的持久化,所以使用起来非常方便。

官方简介链接:

apscheduler.readthedocs.io/en/3.3.1/

安装

1)利用pip安装:(推荐)

2)基于源码安装:

https://pypi.org/project/APScheduler/

4种组件

APScheduler有四种组件:

1)triggers(触发器):

触发器包含调度逻辑,每一个作业有它自己的触发器,用于决定接下来哪一个作业会运行,除了他们自己初始化配置外,触发器完全是无状态的。

2)job stores(作业存储):

用来存储被调度的作业,默认的作业存储器是简单地把作业任务保存在内存中,其它作业存储器可以将任务作业保存到各种数据库中,支持MongoDB、Redis、SQLAlchemy存储方式。 当对作业任务进行持久化存储的时候,作业的数据将被序列化,重新读取作业时在反序列化。

3)executors(执行器):

执行器用来执行定时任务,只是将需要执行的任务放在新的线程或者线程池中运行。 当作业任务完成时,执行器将会通知调度器。 对于执行器,默认情况下选择ThreadPoolExecutor就可以了,但是如果涉及到一下特殊任务如比较消耗CPU的任务则可以选择ProcessPoolExecutor,当然根据根据实际需求可以同时使用两种执行器。

4)schedulers(调度器):

调度器是将其它部分联系在一起,一般在应用程序中只有一个调度器,应用开发者不会直接操作触发器、任务存储以及执行器,相反调度器提供了处理的接口。 通过调度器完成任务的存储以及执行器的配置操作,如可以添加。修改、移除任务作业。 

APScheduler提供了多种调度器,常用的调度器有:

简单的例子

执行后的效果:

很简单有没有,先初始化,然后add_job,最后start就好了,那下面,再详细讲解下不同组件提供的功能吧;

定时任务

trigger提供任务的触发方式,共有3种方式:

date:只在某个时间点执行一次,用法:run_data(datetime|str)

interval: 每隔一段时间执行一次,用法:weeks=0 | days=0 | hours=0 | minutes=0 | seconds=0, startdate=None, enddate=None, timezone=None

cron: 使用同linux下crontab的方式,即定时任务;,用法:(year=None, month=None, day=None, week=None, dayofweek=None, hour=None, minute=None, second=None, startdate=None, enddate=None, timezone=None)

一般来说,使用的比较多的是interval方式,可以重点留意下;

定时任务实战

1)APScheduler怎么设置范围时间任务,比如我想要在10:00~11:00这个范围的时间内随机一个时间点去执行任务

2)如果不想具体的时间,而是某个范围的话:

3)区间时间

任务操作

添加任务add_job

add_job可以返回一个apscheduler.job.Job实例,因而可以对它进行修改或者删除,而使用修饰器添加的任务添加之后就不能进行修改。

获得任务列表get_job

可以通过getjobs方法来获取当前的任务列表,也可以通过get_job()来根据job_id来获得某个任务的信息。

并且apscheduler还提供了一个print_jobs()方法来打印格式化的任务列表。

修改任务 modify_job

修改任务的属性可以使用apscheduler.job.Job.modify()或者modify_job()方法,可以修改除了id的其它任何属性。

删除任务remove_job

删除调度器中的任务有可以用remove_job()根据job ID来删除指定任务或者使用remove(), 如果使用remove()需要事先保存在添加任务时返回的实例对象,任务删除后就不会在执行。

任务的暂停pause_job 和继续resume_job

暂停与恢复任务可以直接操作任务实例或者调度器来实现。 当任务暂停时,它的运行时间会被重置,暂停期间不会计算时间。

任务的修饰modify和重设reschedule_job

调度器操作

开启 scheduler.start()

可以使用start()方法启动调度器,BlockingScheduler需要在初始化之后才能执行start(), 对于其他的Scheduler,调用start()方法都会直接返回,然后可以继续执行后面的初始化操作;

关闭 scheduler.shotdown(wait=True | False)

使用下边方法关闭调度器:

默认情况下调度器会关闭它的任务存储和执行器,并等待所有正在执行的任务完成,如果不想等待,可以进行如下操作:

暂停 scheduler.pause()

继续 scheduler.resume()

豆瓣自动回复

看了那么多APScheduler的简介,上面也有例子了,结合第一部分豆瓣的例子,不难写出下面的代码:

效果如下:

的确是每隔10S发送一次,good~

做到这里,你以为完事了? 嗯,怎么说呢,如果是一个帖子的话,是完事了,但是如下是以下2种场景之一,还需要折腾:

那这两种情况,会导致什么问题?当然是触发豆瓣的防爬虫啦:

目前发现,每次评论就算相隔1分钟,只要满3次,就一定会弹出这个验证码进行验证;

获取验证码ID

按照一开始的套路,那我们F12看下输入验证码后发起请求的参数:

发现会在请求的带上一个叫captcha-solution字段,value就是验证码内容,那我们就异想天开的试试,用postman在body上加上这个验证码参数值,看能否评论?

验证码信息:

postman请求内容:

点击send,然后原来的网页刷新一下,结果如下:

对的,内容没有变,因为这样肯定是不生效的,哪有那么容易;

验证码场景:

那服务器怎么区分A和B?那就是用cookie;

cookie是标示唯一身份的,比如有些网站,登录一次后会自动登录,但是如果清除了cookie,就无法自动登录了,而且这cookie是个别人不一样的; 说到这里,服务器后台生成验证码的流程就很容易理解了:

更多的验证码生成信息,可以读一下这篇文章;

此时,可能你有疑问,用postman的时候,cookie应该是跟PC点击发送是一样的,但是为什么还不行?

因为cookie只是最简单的绑定条件,这么看来,豆瓣还有其他条件的,那我们重新看一下,PC点击发送的时候,除了验证码,还有发送什么?

上面这些信息都是点击发送时,请求里面的body,大致看了下,之前分析的时候,少了captcha-id这个参数,猜测这是就是关键;

简单尝试了下,发现这个captcha-id每次都会不一样,而且从请求里面也看不出啥,既然这个ID可能跟验证码有绑定关系,那我们就解析下这个网页的结构,看下能不能找到这个字段?

点击F12,定位到验证码这块:

看了下右侧的属性,好像没有什么问题,想想,既然不是图片,又要绑定ID属性,那可能就是输入验证码那里了,继续看~

一定位到输入验证码的框里面,嘤嘤嘤,看发现了啥,这不就是想要的captcha-id吗?

爱是怀疑,那我们就来用postman验证一下吧;

页面刷新下:

哈哈哈哈,可以了,再次嘤嘤嘤~

那现在的逻辑,应该是修改成这样:

既然如此,对比下需要输入验证码跟不需要验证码时的网页结构吧;

需要验证码:

不需要验证码:

简单做了下实验,看看能不能获取到这个captcha,有以下代码:

把response打印出来,真的是没有这个值,这里且慢,一开始JB的想法是,没有这个值,就说明这块数据是JS生成的,那我们研究看下怎么获取JS生成的网页数据,然后就霹雳吧啦的介绍selenium;

事实证明,并不需要那么复杂(浪费了半天时间了。。),还是上面这串代码:

把response打印出来,结果发现内容长这样:

嗯,认真点看,发现都是编码过的,但是JB一开始并没有细看,一看获取是空的,就认定是JS加载的,其他这里,只需要改成这样就好了:

效果图,这样就能看到中文了:

这个解决方案,花了半天无意发现的,算是get 到一个小点了,以后request后一定要decode,不用还用selenium就跑远了;

获取二维码下载地址

按照下面的内容,就可以写出这样的代码:

然后再想需要回复的帖子回复三次,让验证码出现,然后再执行这个脚本,不然验证码不出现,就会获取为[]的:

这样,就能获取到验证码图片啦,按照上面说的,如果是有验证码,就获取图片链接跟验证码ID,如果没有,则直接post请求,因此不难写出下面的代码;

效果图:

Ok,这样就能获取到二维码图片跟图片对应的ID了,那接下来要干嘛?

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181030G0XOA000?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券