Python爬虫教程:你还在苦苦拉票吗?刷票小程序案例原理剖析!
你还在苦苦拉票吗?
- 前言
- 剖析投票原理
- 处理思路
- 具体实战
- 主要流程
- 具体细节python
- 代码实例python
- 具体细节java
- 代码实现java
- 总结
很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!??¤
QQ群:623406465
前言
现在生活中离不开各类的比赛,然而,各个比赛离不开投票,我们会清一色有时候找到我们的朋友在朋友圈发—帮宝贝投一票,帮某某老师,学生投一票。又或许你自己参加比赛,你在为你自己拉票。
剖析投票原理
作为一名程序员,你是否想为自己的生活开一点G呢?熟悉网络请求的我们,应该从问题根源分析问题。对于一个投票的网站。大致分为两类:
- 登录类:
这类网站是确实不太好操作,一般是每个账户每天能够刷若干票。因为账户的资源是有限的,我们很难通过获取大量的账户资源为我们服务。况且,一般的登录稍微大厂或者技术高点其中的js加密会比较复杂,对于普通人js水平不高很难行的通这条路。比如常见需要微信登录,qq登陆的网站投票,就很难开挂。 - 非登录类:
并不是所有网站都有腾讯的登录授权的,有很多他们自己的官网他们自己就是一个体系。这类网站普通人或许也感觉不到差异:投几票之后也不能投。然后纷纷找朋友帮忙投。剖析这类网站,既然没有登录机制,那么它就是根据ip机制进行鉴定。因为正常你的公网ip相对来说是稳定。所以正常一个用户只能投固定的几票。或许高级一点他会和浏览器信息结合鉴定,但这种还是比较少的。
处理思路
既然原理已经剖析完成,那么剩下的就是设计程序的问题了,对于一个点击投票的事件,它的实质就是一次http(post)请求,然后后台对数据进行更改。那么我们就可以对这个操作流程进行抓包,分析这个请求是那种类型,需要那些参数。然后根据这个请求模拟写出请求。
然而最重要的就是ip代理,你要用代理的ip去访问那个接口,让对方以为是你代理的那个ip再对他访问,所以你需要维护一个代理ip池。对于代理ip池,并不是什么高大上的东西,准确的来说就是一个集合中包含一些可用的ip,能够供我使用。市面上也有很多出售代理ip,也不贵。我用的是蘑菇代理。
具体实战
主要流程
碰巧,最近参加的一个比赛就有拉票环节,如果人为手动拉票的话效率地下,并且你肯定也不会愿意天天去舔人家求情。那就自己分析一波!
- 首先,打开你在的网站(有的手机端,电脑端好抓包可调),谷歌或者其他浏览器F12抓包,点击network,xhr准备(肯定是ajax请求不用想)。
- 分析这个请求的重要参数.(header)
找到url和几个参数,就可以准备程序了。模拟请求了
具体细节python
因为这是多次请求,所以要考虑性能的问题和效率问题。不能让异常漫天飞,中断,ip白白浪费,或者苦苦等待吧。
对于代理ip,各家卖的虽然有些差异但是大体相同。大致均为卖数量,然后每个ip从开始被用后能够维持几分钟的使用。并且有的ip是不能用的,有的是高延迟的,这些在写程序的时候都要过滤掉。这里面就要考虑下这个程序额设计。
- 多线程:
python虽然多线程有个全局锁大大的影响效率。但是对于io请求型多线程还是能有一定的提速的。因为io有大量的线程等待。多线程的模块大致为定义一个线程类,定义初始方法和run函数。然后在外面定义几个线程,让他们跑任务。 - ip处理和资源处理
正常的ip代理是一群ip随机抽取其中作为代理ip,进行爬取任务,然后ip失效从ip池中删除。而url的链接一般放到线程安全的全局容器中一个个抛出。ip放到list或者redis中进行维护,做好try catch异常即可。但是这个刷票只有一个url。并且一个ip只能用有限次数。所以换个思路,url不需要容器维护。而ip用队列维护最好,并且python的队列是线程安全的。所以整个程序的架构也就很清晰了。只需要用个queue解析ip获取的格式进行相应储存。然后被消费,当少于一定个数时,请求api获取ip进行填充。 - 在预处理方面,以前介绍过另一个蘑菇代理使用和ip池类似的问题,可以预先参考。
代码实例python
import requestsimport random
import time
import threading
from queue import Queuedef loadip():
url2
= "http://piping.mogumiao.com/proxy/api/get_ip_al?appKey=f16367295e284173ae450fc38d9098b3&count=20&expiryDate=0&format=1&newLine=2"req
= requests.get(url2)date
= req.json()if(date["code"])!="3001":ipdate2
= date["msg"]global ipdateipdate.extend(ipdate2)
for va in ipdate2:que.put(va)
print(ipdate)
class downspider(threading.Thread):def __init__(self, threadname, que):
threading.Thread.__init__(self)
self.threadname
= threadnameself.que
= quedef run(self):
print(
"start thread" + self.threadname)while True:try:print(self.name,end
="")toupiaospider(que,self.threadname)
except Exception
as e:print(e,
"888")breakdef getproxies():
b
= ipdate[0]b
=que.get()d
= "%s:%s" % (b["ip"], b["port"])global proxiesproxies[
"http"] = dglobal msgmsg
= breturn proxiesdef toupiaospider(que,threadname):
if (que.qsize() < 15): # 拓展ip池loadip()
proxies2
=getproxies()for i in range(0,5):try:#formData[
"times"]=ireq
= requests.post(url, headers=header, data=formData, proxies=proxies2, timeout=1.5)res
= req.json()if res["res"]==2001 or req.status_code!=200:#ipdate.remove(msg)
continueprint(threadname,res,que.qsize())
except Exception
as e:print(
"errror",e)# ipdate.remove(msg)
if __name__ == "__main__":ipdate
= []msg
= {}proxies
= {"http": ""}stadus
= 0que
= Queue()threads
=[]#线程url
="http://yunxin.163.com/api/vote/update"header
= {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"}formData
= {"Referer":"http://yunxin.163.com/promotion/minichallenge/gallery?from=groupmessage&isappinstalled=0","id":"17","times":"1","activity":"minichallenge1"}
proxies
= {"http": "182.247.92.99:21136",}
loadip()
time.sleep(
5)threadList
= ["thread-1","thread-2","thread-3","thread-4","thread-4"]for j in threadList:thread
= downspider(j, que)thread.start()
threads.append(thread)
for t in threads:t.join()
结果
具体细节java
在java中比较棘手的就是java自身对json和http请求的处理不太方便,需要借助第三方jar,并且一些操作稍显的繁琐。
首先java要弄清几点:
- 代理方式:
java主要有两种代理方式,一种是jdk全局代理,另一种是net包下的proxy代理。对于多线程程序并且ip只能用一次的当然是用net的proxy代理。 - 解析json
通过api获取ip,格式固定的,需要借助fastjson解析json串获取需要的信息。 - 线程安全问题。你可以用线程安全的blockqueue,当然其实你可以在操作队列的方法加上synchronized关键字也可以。你可以定义固定的线程每个线程任务多个。也可以用线程池定义多个线程类,每个线程完成一个任务。
- 网络请求虽然urlconnection可以实现,但是太繁琐,远比jsoup复杂。所以这里使用jsoup。
针对上面的问题。写了个demo测试进行预备,对于获取ip的api,大致这种格式
首先你要下载fastjson和jsoup的jar包。或者加入maven依赖。(可在maven官网下jar包)
然后写个demo跑一下
package com.bigsai;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
publicclass test2 {staticint va=1;publicstaticvoid main(String[] args) {String ti
="{"code":"0","msg":[{"port":"40034","ip":"114.237.64.247"},{"port":"33257","ip":"223.240.210.250"},{"port":"39618","ip":"113.101.255.11"},{"port":"43151","ip":"183.135.106.62"},{"port":"41795","ip":"182.108.44.227"}]}";JSONObject jsonObject
= JSON.parseObject(ti);String code
=(String) jsonObject.get("code");JSONArray jsonArray
=jsonObject.getJSONArray("msg");for(Object te:jsonArray){
JSONObject team
=(JSONObject) te;String ip
=team.getString("ip");int port=team.getInteger("port");System.
out.println(team "" ip "" port);}
ExecutorService ex
= Executors.newFixedThreadPool(10);for(int i=0;i<200;i ){
threadtest threadtest
=new threadtest();ex.execute(threadtest);
}
ex.shutdown();
}
static synchronized void addva()//去掉注释试试{
va ;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() "" va);
}
staticclass threadtest implements Runnable{
@Override
publicvoid run() {
addva();
}
}
}
观察结果。打印,这些边角问题你就明白了。就可以设计java程序了。
代码实现java
package com.bigsai;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
publicclass wangyi {// 改成你自己的token已经被我改了static String ipurl = "http://piping.mogumiao.com/proxy/api/get_ip_al?appKey=f16367295e284173ae3&count=20&expiryDate=0&format=1&newLine=2";
/**
* java的多线程和python略有不同,但也可以改成相似的 py是5个线程每个线程死循环任务,而java用线程池每个线程一个任务,只不过new了很多对象
*
* @param args
*/
static Queue q1;
publicstaticvoid main(String[] args) {
q1=new ArrayDeque();//队列存放结构体 ip和port
ExecutorService ex= Executors.newFixedThreadPool(10);
for(int i=0;i<200;i )
{
try {
Proxy proxy=getproxies(q1);//获得代理ip
vote vote=new vote(proxy);
ex.execute(vote);
}
catch (Exception e)
{e.printStackTrace();}
}
ex.shutdown();
}
static synchronized Proxy getproxies(Queue q1) throws IOException// 上锁获得代理,因为ip只用一次,也可使用自带的线程安全队列
{
if (q1.size() < 15)// 扩充ip池
{
String jsonva = Jsoup.connect(ipurl).timeout(2500).get().text();
JSONObject jsonObject = JSON.parseObject(jsonva);
String code = (String) jsonObject.get("code");// 状态吗
if (code.equals("0")) {// 正常返回接口
JSONArray jsonArray = jsonObject.getJSONArray("msg");
for (Object jsonobj : jsonArray) {
JSONObject team = (JSONObject) jsonobj;
String ip = team.getString("ip");
int port = team.getInteger("port");
proxynode node = new proxynode(ip, port);
q1.add(node);
}
} else
returnnull;
}
proxynode proxynode = q1.poll();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxynode.ip, proxynode.port));
return proxy;
}
staticclass proxynode// 一个node储存ip和端口
{
String ip;
int port;
proxynode(String ip, int port) {
this.ip = ip;
this.port = port;
}
}
staticclass vote implements Runnable {
Proxy proxy;
vote(Proxy proxy) {
this.proxy = proxy;
}
publicvoid dovote() throws IOException {
//Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("host", 8888));
try {
Connection connect = Jsoup.connect("http://yunxin.163.com/api/vote/update").timeout(2000);
Map date=new HashMap();
date.put("id","17");
date.put("times","1");
date.put("activity","minichallenge1");
date.put("Referer","http://yunxin.163.com/promotion/minichallenge/gallery?from=groupmessage&isappinstalled=0");
date.put("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");
connect.data(date);
connect.ignoreContentType(true);
connect.proxy(proxy);
Document doc=connect.post();
System.out.println(Thread.currentThread().getName() "" proxy.address() "" doc.text());
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
publicvoid run() {
try {
for(int i=0;i<5;i ) {
dovote();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
结果
总结
在写爬虫还是python方便和简单。因为py对json支持较好(字典),而java强对象类型语法要求较严。但是在多线程方面java肯定是秒杀py的。因为py的多线程是(假)多线程。想提高速度的可以研究多进程。
这类问题本质不难的,做过一次就很简单了。这只是其中一种案例。提供一些思想和解决思路。遇到不同的问题可能需要不同的结构,方式去解决,这就需要融汇贯通。
以上是 Python爬虫教程:你还在苦苦拉票吗?刷票小程序案例原理剖析! 的全部内容, 来源链接: utcz.com/z/530567.html