0%

Python爬虫实战三之实现山东大学无线网络掉线自动重连

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

如下为原文。

综述

最近山大软件园校区 QLSC_STU 无线网掉线掉的厉害,连上之后平均十分钟左右掉线一次,很是让人心烦,还能不能愉快地上自习了?能忍吗?反正我是不能忍了,嗯,自己动手,丰衣足食!写个程序解决掉它! 假若你不能连这个无线,那就照照思路啦~

决战前夕

首先我们看一下那个验证页面是咋样滴,上个图先 20150920192557 嘿,这界面还算可以把,需要我们输入的东西就是俩,一个就是学号,另一个是身份证号后六位,然后就可以登录,享受免费的无线网啦。 不过不知道谁设置了个登录时长,一段时间后就会掉线了,于是,自动模拟登陆系统就要应运而生啦。 来,我们先点击一下连接,看一下浏览器怎么工作的。 按下 F12,监听网络,我们点击第一个响应,也就是 login.jsp,看一下。 20150920192957 我们具体看一下 headers,里面 form 提交了什么东西,真的是茫茫多的数据啊。 20150920193146 嗯,一目了然 POST 的数据和提交的地址。 让我们来分析几个数据吧:

ClientIP:当前客户端的 IP 地址,在山大软件园校区这个地址是 211.87 开头的

timeoutvalue:连接等待时间,也就是俗话说的 timeout

StartTime:登录时间,也就是在你登录的那一刻的时间戳,这个时间戳是 13 位的,精确到了毫秒,不过一般是 10 位的,我们加 3 个 0 就好了

shkOvertime:登录持续时间,这个数据默认是 720,也就是 12 分钟之后,登录就失效了,自动掉线,我们可以手动更改

username:学号

password:密码,也就是我们身份证号后六位

我们需要在登录的时候把 form 表单中的所有信息都 POST 一下,然后就可以完成登录啦。 万事俱备,只欠东风,来来来,程序写起来!

一触即发

说走咱就走啊,天上的星星参北斗啊! 登陆地址:Request URL:http://192.168.8.10/portal/login.jsp?Flag=0 首先,我们需要验证一下 IP 地址,先写一个获取 IP 地址的函数,首先判断当前 IP 是不是 211.87 开头的,如果是的话,证明连接的 IP 是有效的。 首先我们写一个获取本机 IP 的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
self.ip_pre = "211.87"
#获取本机无线IP
def getIP(self):
local_iP = socket.gethostbyname(socket.gethostname())
if self.ip_pre in str(local_iP):
return str(local_iP)
ip_lists = socket.gethostbyname_ex(socket.gethostname())

for ip_list in ip_lists:
if isinstance(ip_list, list):
for i in ip_list:
if self.ip_pre in str(i):
return str(i)
elif type(ip_list) is types.StringType:
if self.ip_pre in ip_list:
return ip_list

这个方法利用了 gethostbyname 和 gethostbyname_ex 方法,获取了各个网卡的 IP 地址,遍历一下,找到那个 211.87 开头的 IP,返回 接下来,获取到 IP 之后,我们便可以构建 form,然后进行模拟登陆了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#模拟登录
def login(self):
print self.getCurrentTime(), u"正在尝试认证QLSC_STU无线网络"
ip = self.getIP()
data = {
"username": self.username,
"password": self.password,
"serverType": "",
"isSavePass": "on",
"Submit1": "",
"Language": "Chinese",
"ClientIP": self.getIP(),
"timeoutvalue": 45,
"heartbeat": 240,
"fastwebornot": False,
"StartTime": self.getNowTime(),
#持续时间,超过这个时间自动掉线,可进行设置
"shkOvertime": self.overtime,
"strOSName": "",
"iAdptIndex": "",
"strAdptName": "",
"strAdptStdName": "",
"strFileEncoding": "",
"PhysAddr": "",
"bDHCPEnabled": "",
"strIPAddrArray": "",
"strMaskArray": "",
"strMask": "",
"iDHCPDelayTime": "",
"iDHCPTryTimes": "",
"strOldPrivateIP": self.getIP(),
"strOldPublicIP": self.getIP(),
"strPrivateIP": self.getIP(),
"PublicIP": self.getIP(),
"iIPCONFIG":0,
"sHttpPrefix": "http://192.168.8.10",
"title": "CAMS Portal"
}
#消息头
headers = {
'User-Agent' : 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36',
'Host': '192.168.8.10',
'Origin': 'http://192.168.8.10',
'Referer': 'http://192.168.8.10/portal/index_default.jsp?Language=Chinese'
}
post_data = urllib.urlencode(data)
login_url = "http://192.168.8.10/portal/login.jsp?Flag=0"
request = urllib2.Request(login_url, post_data, headers)
response = urllib2.urlopen(request)
result = response.read().decode('gbk')

比较多的内容就在于 form 表单的数据内容以及请求头,后来利用 urllib2 的 urlopen 方法实现模拟登陆。 如果大家对此不熟悉,可以参见 Urllib 的基本使用 这样,登录后的结果就会保存在 result 变量中,我们只需要从 result 中提取出我们需要的数据就可以了。

乘胜追击

接下来,我们就分析一下数据啦,结果有这么几种:

1.登录成功 2.已经登录 3.用户不存在 4.密码错误 5.未知错误

好,利用 result 分析一下结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#打印登录结果
def getLoginResult(self, result):
if u"用户上线成功" in result:
print self.getCurrentTime(),u"用户上线成功,在线时长为",self.overtime/60,"分钟"
elif u"您已经建立了连接" in result:
print self.getCurrentTime(),u"您已经建立了连接,无需重复登陆"
elif u"用户不存在" in result:
print self.getCurrentTime(),u"用户不存在,请检查学号是否正确"
elif u"用户密码错误" in result:
pattern = re.compile('<td class="tWhite">.*?2553:(.*?)</b>.*?</td>', re.S)
res = re.search(pattern, result)
if res:
print self.getCurrentTime(),res.group(1),u"请重新修改密码"
else:
print self.getCurrentTime(),u"未知错误,请检查学号密码是否正确"

通过字符串匹配和正则表达式,我们分辨并提取出了上述五种情况。 增加循环检测 既然是检测网络是否断开,那么我们只需要每隔一段时间检测一下就好了,那就 10 秒吧。 因为这个 10 秒是可配置的,为了方便配置,统一配置到init方法里面。

1
2
#检测间隔时间,单位为秒
self.every = 10

然后,我们写一个循环来检测一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
while True:
nowIP = self.getIP()
if not nowIP:
print self.getCurrentTime(), u"请检查是否正常连接QLSC_STU无线网络"
else:
print self.getCurrentTime(),u"成功连接了QLSC_STU网络,本机IP为",nowIP
self.login()
while True:
can_connect = self.canConnect()
if not can_connect:
nowIP = self.getIP()
if not nowIP:
print self.getCurrentTime(), u"当前已经断线,请确保连接上了QLSC_STU网络"
else:
print self.getCurrentTime(), u"当前已经断线,正在尝试重新连接"
self.login()
else:
print self.getCurrentTime(), u"当前网络连接正常"
time.sleep(self.every)
time.sleep(self.every)

其中我们用到了 canConnect 方法,这个就是检测网络是否已经断开的方法,我们可以利用 ping 百度的方法来检测一下。 方法实现如下

1
2
3
4
5
6
7
8
9
#判断当前是否可以联网
def canConnect(self):
fnull = open(os.devnull, 'w')
result = subprocess.call('ping www.baidu.com', shell = True, stdout = fnull, stderr = fnull)
fnull.close()
if result:
return False
else:
return True

好啦,所有的要点我们已经逐一击破,等着凯旋吧

收拾战场

好了,所有的代码要点已经被我们攻破了,接下来就整理一下,让他们组合起来,变成一个应用程序吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
__author__ = 'CQC'
#-*- coding:utf-8 -*-

import urllib
import urllib2
import socket
import types
import time
import re
import os
import subprocess

class Login:

#初始化
def __init__(self):
#学号密码
self.username = '201200131012'
self.password = 'XXXXXX'
#山大无线STU的IP网段
self.ip_pre = '211.87'
#登录时长
self.overtime = 720
#检测间隔时间,单位为秒
self.every = 10

#模拟登录
def login(self):
print self.getCurrentTime(), u"正在尝试认证QLSC_STU无线网络"
ip = self.getIP()
data = {
"username": self.username,
"password": self.password,
"serverType": "",
"isSavePass": "on",
"Submit1": "",
"Language": "Chinese",
"ClientIP": self.getIP(),
"timeoutvalue": 45,
"heartbeat": 240,
"fastwebornot": False,
"StartTime": self.getNowTime(),
#持续时间,超过这个时间自动掉线,可进行设置
"shkOvertime": self.overtime,
"strOSName": "",
"iAdptIndex": "",
"strAdptName": "",
"strAdptStdName": "",
"strFileEncoding": "",
"PhysAddr": "",
"bDHCPEnabled": "",
"strIPAddrArray": "",
"strMaskArray": "",
"strMask": "",
"iDHCPDelayTime": "",
"iDHCPTryTimes": "",
"strOldPrivateIP": self.getIP(),
"strOldPublicIP": self.getIP(),
"strPrivateIP": self.getIP(),
"PublicIP": self.getIP(),
"iIPCONFIG":0,
"sHttpPrefix": "http://192.168.8.10",
"title": "CAMS Portal"
}
#消息头
headers = {
'User-Agent' : 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36',
'Host': '192.168.8.10',
'Origin': 'http://192.168.8.10',
'Referer': 'http://192.168.8.10/portal/index_default.jsp?Language=Chinese'
}
post_data = urllib.urlencode(data)
login_url = "http://192.168.8.10/portal/login.jsp?Flag=0"
request = urllib2.Request(login_url, post_data, headers)
response = urllib2.urlopen(request)
result = response.read().decode('gbk')
self.getLoginResult(result)


#打印登录结果
def getLoginResult(self, result):
if u"用户上线成功" in result:
print self.getCurrentTime(),u"用户上线成功,在线时长为",self.overtime/60,"分钟"
elif u"您已经建立了连接" in result:
print self.getCurrentTime(),u"您已经建立了连接,无需重复登陆"
elif u"用户不存在" in result:
print self.getCurrentTime(),u"用户不存在,请检查学号是否正确"
elif u"用户密码错误" in result:
pattern = re.compile('<td class="tWhite">.*?2553:(.*?)</b>.*?</td>', re.S)
res = re.search(pattern, result)
if res:
print self.getCurrentTime(),res.group(1),u"请重新修改密码"
else:
print self.getCurrentTime(),u"未知错误,请检查学号密码是否正确"

#获取当前时间戳,13位
def getNowTime(self):
return str(int(time.time()))+"000"

#获取本机无线IP
def getIP(self):
local_iP = socket.gethostbyname(socket.gethostname())
if self.ip_pre in str(local_iP):
return str(local_iP)
ip_lists = socket.gethostbyname_ex(socket.gethostname())

for ip_list in ip_lists:
if isinstance(ip_list, list):
for i in ip_list:
if self.ip_pre in str(i):
return str(i)
elif type(ip_list) is types.StringType:
if self.ip_pre in ip_list:
return ip_list

#判断当前是否可以联网
def canConnect(self):
fnull = open(os.devnull, 'w')
result = subprocess.call('ping www.baidu.com', shell = True, stdout = fnull, stderr = fnull)
fnull.close()
if result:
return False
else:
return True

#获取当前时间
def getCurrentTime(self):
return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))

#主函数
def main(self):
print self.getCurrentTime(), u"您好,欢迎使用模拟登陆系统"
while True:
nowIP = self.getIP()
if not nowIP:
print self.getCurrentTime(), u"请检查是否正常连接QLSC_STU无线网络"
else:
print self.getCurrentTime(),u"成功连接了QLSC_STU网络,本机IP为",nowIP
self.login()
while True:
can_connect = self.canConnect()
if not can_connect:
nowIP = self.getIP()
if not nowIP:
print self.getCurrentTime(), u"当前已经掉线,请确保连接上了QLSC_STU网络"
else:
print self.getCurrentTime(), u"当前已经掉线,正在尝试重新连接"
self.login()
else:
print self.getCurrentTime(), u"当前网络连接正常"
time.sleep(self.every)
time.sleep(self.every)

login = Login()
login.main()

来,我们来运行一下,看下效果吧! 执行

1
python login.py

当前是可以联网的,我分别在网页上操作执行了断开,操作,程序自动检测到掉线,自动重新连接。 接下来我直接断开了 QLSC_STU 网络的链接,程序同样检测到 QLSC_STU 这个热点没有连接上,提示用户链接。 接下来我重新连接上了这个热点,由于刚才已经登录上线,且持续时间较短,网络自动恢复正常。 下面是运行结果: 20150920205618 嗯,这样我们就是实现了自动掉线的检测和模拟登录。

凯旋而归

咿呀伊尔哟,想约妹子上自习吗?那就赶紧来试试吧!一网在手,天下我有!追男神女神都不再是梦想! 如果有问题,欢迎留言讨论,代码肯定有不完善的地方,仅供参考。