|
|
selenium模拟登录( T; R( U3 a7 c$ R) E- ?
QQ空间的反爬做的相对较好,而且由于好友权限的原因,我们要先登录后再进行说说等信息的获取
1 n+ R I+ ^# B2 N- n' X/ ?6 U6 o2 ]
5 z; a/ \7 q& _/ \4 Mselenium是获取登录cookies的一大利器,非常方便
2 R$ L+ o; [0 l* Q3 y. d% E
9 D0 { Y& ?/ u9 f* u! W% v% L
! Z1 x! `) X2 _7 \+ c+ T
( `4 `) S9 M+ v- |! ^: o T在空间的登陆界面可以观察到,登录的窗口与背景窗口是分开的,所以我们需要先切换框架9 _2 x$ N9 @7 H5 G; P2 J
3 @: N% c- R. ]) D/ C% b1 {( Y6 {切换窗口后定位到账号密码登录元素的位置后点击
, l6 a) j v5 \- G( p: |" s; s; p- c/ z" P+ \+ R$ Y
- V5 c% M* x) H. L; Z+ p* f2 `. Z% c" b9 l @
使用send_keys函数把账号和密码写入对应位置后定位登录元素后点击,这里使用自带的get_cookies函数获取到cookies,但是这个cookies需要过滤一下,具体操作看以下代码' w/ z. X7 {0 X5 {" G
. R0 M1 n& V* f! N代码为类的部分节选,完整代码在最后,未声明的变量皆为类的成员变量) ?6 W' n, W/ K5 W6 Y$ l9 F
: `! v! V' T6 G2 ]& R$ n: mde login_func(self,z):" M# w: q3 M. t8 K( f$ O+ b: U
browser = webdriver.Chrome(). L7 B. y$ x" M1 M2 F5 B& |
browser.maximize_window()
' T7 @9 e7 }: ? X2 i3 B+ y browser.get(self.login_url)- }* ^' d. q" W, P7 n
time.sleep(1.2)+ R: v( z- ^0 R$ e: m& E
browser.switch_to.frame('login_frame')
# k0 Z9 m, |& j* L8 U browser.find_element_by_id('switcher_plogin').click()* Z+ L2 G; x6 v3 d' ]! D! |/ i
time.sleep(1)" H6 ^, f2 ]. W4 `2 E
browser.find_element_by_id('u').send_keys(self.number)
! }3 B$ H" f& }6 T( o: O; N. \ browser.find_element_by_id('p').send_keys(self.password)
4 G% v/ T! e5 w" I1 y2 o time.sleep(1)
' D" B3 b1 q6 l- Z5 ` browser.find_element_by_id('login_button').click()
; ]6 Q, u( M( R" B time.sleep(1)
. G; Y8 ~! T/ R( x% ]2 W cookies_list = browser.get_cookies()
* S* u% b6 X/ R1 b2 v8 d. R
" C8 j* B# z, Z3 Q8 O+ P for cookie in cookies_list:
* {% k% ?& \" H' N0 x7 ] if 'name' in cookie and 'value' in cookie:
3 V2 p6 G' |) | self.cookies[cookie['name']] = cookie['value']3 G( z: a( E! s
# print(self.cookies)6 B! @. p) V3 S: ~# ^7 J+ H& O
with open('cookie_dict_{}.txt'.format(z),'w') as f: # 这里是为了一次性多刷几次cookies建立一个cookies池,便于加快爬虫速度,后面再提/ X. A- l4 y A
json.dump(self.cookies,f)
- Z$ k" ?" e) U! A" _; l" w, \, M# browser.close(): w( R) M; Z. R& z9 E
% X( k% q' |: |6 ^% Y4 y1
6 ^3 L3 ?: F3 c; b2* R- {" w' Y5 C, t- b% S% }
3+ k% @1 k$ @: ^) @$ i
4
7 z+ g0 w) j/ h" y5
+ K. t3 l) x7 z, n6 {; M' c6
8 d8 x% {5 U$ `, K0 g( r7
9 o& L- l" i* e8 s, @8 G, A! c8, C/ U6 O9 w% l6 {6 O4 {, {; m
9
. {: T; U0 g5 \$ {2 x4 O1 }! k10, T$ w* q8 n1 @ x% B( g2 N
11
. D3 h) _5 E8 E6 t' w7 f% u12
; Z- @5 w0 r, ~; O2 l" `13
, j: K% ~3 x0 |; ?
* L3 _7 D2 ?% X4 d$ @' Y: u14 15
" X2 z" U3 g; ~. g7 E, {( x
) m' k/ b8 \5 `: S' [3 Q e16 17
% V+ ]6 S4 e/ T$ S18
+ _% G1 e- F( ]* m190 e. I2 s5 ~! [! H+ `& @+ ^
20" o a: ^5 o/ t8 p& d
21
* D1 x- [, k: N( @& |22
! W0 w/ N$ r, P' J( v) n# t说说内容获取4 {* z, z% e2 S6 o) J1 ^
在打开开发者工具后,在众多XHR对象中发现emotion_cgi......里面的msglist即是说说的内容
3 D( W% g9 B. N8 Y- X2 _4 h6 Q8 D2 M8 c: V) b2 I
}/ M' o$ G L# ]. @
( e; q1 q9 M ^& P- [( i4 h2 x/ }0 J
' S- ^' Y' b: z
" M* B1 R+ ]: b2 M" l/ t
这里的msglist是个列表,里面有0-20条不等的说说,可能跟空间发的说说的形式问题相关,至多不超过20条
# C+ I$ b6 e: u2 z8 w& f
# S3 U4 ?0 J+ G3 S$ p稍微猜测一下这里的参数的含义,一眼明了的我就不说了,我不清楚的也没有肆意揣度
5 i, D' `) e0 z2 i- \ \# i m) `6 y
cmtnum 转发数+ P( ]! [* Z" a$ K6 i$ `7 V
6 h# B& i8 |5 q* M& l8 n7 hcommentlist 评论列表,里面是每条说说
3 J# g, _! A' @9 m; l- \7 q9 W" s+ j& e _, @$ {9 G- @
的内容conlist 内容的一个列表,里面有两个参数,一个是内容一个不知道有啥作用,取内容的话直接取下面的内容也是一样的
" |8 [ e$ s" ^9 d% R i9 s* {$ e5 l# X
created_time 说说发表的时间戳
7 v. u) r0 d, g! o* y9 g3 B$ A$ `2 m+ S9 ?( `
isEditable 是否编辑过
4 F1 [) u7 h/ y& U
& p0 i# R+ @( Q" E* _; Z/ klbs 位置信息" m$ X" B/ t" X8 u x
( `/ ]7 u% G! j ]2 mname 你给的备注,没备注就是昵称# D$ N; u( d3 D/ V- Y
: ]8 E9 {* y4 T9 T1 m8 H
pic 如果发的说说有图片则在这个键下面,但是如果没有图片则没有这个键5 \3 V" h2 [: y; M: Z
1 W+ G5 T) x& e5 i. p; o+ kpictotal 图片数量,没有图片则没有这个键
2 b$ P @5 S h$ i2 ]
8 W: G0 ]% J( W: Q# m7 v# Krt_sum 猜测是转发数量
# r$ o) I& m0 b T# p9 t2 c6 o8 ?/ s+ I8 F4 o$ Z* l! ]* O- z
source_appid 说说来自的app标识
& ^9 [7 }7 B8 {8 W5 ^9 K/ T
# z2 F6 {% C0 N0 p2 y" A5 msource_name 说说来自的设备名称% |7 R( ], {1 D, k! ?& A
" Y5 |- `) m7 s2 M+ n1 |1 K
source_url 说说来自的网址1 r7 p% w, x2 l, Z1 h* A4 ^! r
6 b* m; {4 |- c3 z! V$ E
tid这个是每个说说独一无二的标识,可能是根据某些变量使用特定的算法得出的,直接使用即可
1 l( I$ ^) h/ t7 |5 s
[0 B. e- j, ~; R& o0 auin 该说说的作者QQ7 z+ @- |( j: q2 O# i. {
) o. t6 w8 O5 }& C7 {
当然如果是转发的说说,这里还会多别的一些键值,我这里未对转发说说进行处理,只是单纯地取出该QQ转发时发送的内容,有兴趣的朋友可以加以改进
0 {/ H* L0 I0 O- A8 _" G# |: }) g# V ~$ z' u3 a# d+ c- J
下面我们看一下这个内容的获取网址构成
& C, D9 T. U, Y! h) i% Q' |& G
* Q" Q( ^$ _5 D: u- ]9 k7 |+ M在Headers选项中可以看网址的构成参数
- D; j; z. @! n; w' _
7 m9 P# z$ i+ }) `/ r经过尝试发现,uin后面对应目标QQ号,sort可能对应排序方式,我采取的默认值0,pos这是个关键参数,其改变决定了返回数据的范围,num是返回的说说数量,我选用的是默认值,不知道增大会有什么变化,读者可以尝试( D- W! C7 F& o( [7 D
9 v& g' b6 c7 Q1 V* y2 g" U
最后一个关键参数是g_tk,这是个加密参数,有了这个才能正确登录/ o' l' A$ ]9 h0 y" y2 d- T
9 t E) U) Q* e8 x" h
2 b% L8 @/ r- J7 n# y2 X! Z: c5 P& m; Y- X+ Y
破解g_tk& r2 t1 s" _3 G) ^2 ?( N e
网上的搜索发现是js,破解的方法见下图% j6 b% H1 ~ X- [' ~
7 A4 @0 O4 Q" x+ V/ ?随意点开一个人的空间,进行如下操作, }1 K* N2 C9 i" v
8 R! O2 i4 z p
" b) n) X6 b; \$ \! T
* v/ S& v7 B2 B; Z4 g% v" _搜索g_tk=后面的关键词 q! Y4 z8 m9 W Q! @0 `$ M
$ M7 W8 U1 z9 D- [% E
# g& N( e5 z1 z3 g1 E
4 k8 `" ^* Z" j8 z/ j" M: U
找到对应的函数,这里的函数读一下之后将其转成对应的python语言即可$ `! i4 k" J% V) W& Y
' C7 H! T1 ^7 hdef get_g_tk(self):1 N! h- Z( ~8 w* ~/ d
p_skey = self.cookies['p_skey']* r5 I9 U7 l7 R# W ^* H$ ]# I, D' H+ m
t = 5381
. E" @; B9 e; M7 p& ` for i in p_skey:- Z _3 K9 z( n7 `' F: A. l
t += (t<<5) + ord(i)
4 ?3 Q2 z; A3 r1 s return t & 2147483647
9 I" [: J* H' s( ~' O1 x1- t' [! r2 ?$ b1 ?+ t( z
2
! z7 A$ r$ g) I9 I% I( k; g3 u2 m( @) H& \ v2 Q( W: z
4& p1 `% {# p: i4 \# f P
50 U2 T9 {4 \) x9 _, N, ^
6
" d2 {' ]$ r6 _说说的评论获取
( H- k# d2 v1 x, g# t这里没什么好说的,数据返回是跟说说一起的,在commentlist的键里面,里面的键值对和外面的类似,这里就不赘述了,值得一提的是,外面的cmtnum返回的评论数是指单独的回复数量,也就是跟楼的评论数量不被统计,跟楼的评论在每条父评论的里面,对应键list_3
) k* f5 V% D s) P3 l$ d4 t q" O/ a- I& s. r% [7 n
F/ s2 @& ^4 s% y9 F
+ c8 d/ e& Z; g说说的点赞人获取
( {+ r" K2 _5 ?5 \, T5 v3 O( \. ?2 l- z9 t( W/ ]
$ J+ f3 {0 g( z5 ?! I% V1 c4 b
框内可以点击,点击后4 |; \6 G1 h2 l7 L
: l C8 l- M, x- S" y8 T: k" }( S1 u0 z, X1 ^6 z
出现3 B2 K" K5 M8 {
同时右边出现一个
1 Q7 a# T1 h& `- U& K" g9 G
; {) b) J+ H. z, H1 K5 o* f9 d o- L
# z6 H1 x) k5 `- T
这里对应的内容为
1 Z# N1 n, c/ w/ _! `& u7 @9 V* f ` y$ P
is_dolike 我是否点赞了
, P# q) R5 |/ Q& T0 t& `$ J$ P! l7 U+ k
like_uin_info 点赞这条说说的朋友的信息(除我以外
# Z" D7 n k& X
L+ L' ]$ N$ Z0 F% itotal_number 总共的点赞数量
9 }7 r5 W. F5 G, |$ v4 f6 V4 x$ z* s- \. ?" X2 X1 v
每位点赞好友里面还有一些信息,我这里就没有赘述,那些键值都看得懂
# z5 S9 Y+ ]& p% T) E0 Y! S5 U, f" i; ]2 [, I
url参数构成
% y+ }1 _1 A5 K# Q( t' n那么还需要知道的就是url的构成,老方法,先看headers
" u" k; C- b* [( j2 L# _" T% _9 u9 s7 l/ U# u8 _; d7 d6 a0 {
" N' t* G% z) g% E4 T; P, [1 W" y
那tid在哪里呢
- I t6 W) A& S4 w* C1 [- ~; O9 I
# T' u6 T4 p P, ^! `; q/ V9 N {之前的msglist里每条说说底下对应都存在一条tid,这里就是它的用武之地了!
! {0 X# D( M; J1 }( x( v( l' t- ]6 L8 j$ f) Z8 D; Z
好友列表获取
* H' ?8 q' a. _6 `1 H我在网上看到过很多个版本) N' D" T9 h: b3 x
/ L1 P6 b! X& W
我自己也都尝试了一下,以下的版本获取到的好友信息与QQ好友是最一致的: c3 y7 g2 C$ N* h. s
. y" d& ~" {* ]$ c$ o5 ~进入自己的空间后在设置中点权限设置
! i" ]/ \& D) M
# |& q2 c; \* @! H4 g( D' _
a" p) L+ w0 }1 L: s0 o3 W5 q1 H" p. s
3 l0 x# Y( C! K5 v L
: V5 i% t5 }$ y: W2 F( t7 C找到对应的项,friendlist里面即是,但是只有50条,如果你点开了xxx个QQ好友并向下滚动后查看url构成
. V6 A4 V% Y: u* Q2 F/ `' c1 E$ L9 C1 L" u: [9 c4 d
就会发现
3 R5 H) ~" e7 S4 H, o7 O1 D) Q" ~+ p$ U8 g; X/ X$ T
1 H9 ~! H, D9 N5 z, v
8 P/ { m5 t/ Loffset偏移量用来查看更多的好友
8 C9 N6 n: i4 `- h5 O* m1 _: F2 r
,如果是最后一页,返回的字典中的键end的值为14 n9 r* ]2 X% ]) a0 B
0 |/ u" W3 F# o/ R8 F7 S- W数据库的存储
" A s% ~' D: J% g( U' _由于对数据库的使用不是十分熟练,这里单纯只是为了存一下,有很多弊端,例如图片的存储. F2 O+ O/ z; B. z) I
$ |2 P4 C6 ^' F$ r7 P9 l而且用的很丑陋,这段代码可以忽略3 d1 A3 ~( p# B1 Q0 G
$ ?' L$ }3 c6 ^) O* I
def check_exist(self,uid):: a$ n: y) P+ w
cursor = self.conn.cursor()
3 X3 o) a5 v0 Q3 \8 `7 ^ p4 \7 L7 m cursor.execute(“show databases”)' K/ w' m' f. Z9 N
content = [i[0] for i in cursor.fetchall()]8 p @, `! ^3 Z4 `3 l- ~
if uid not in content:; N0 U+ E9 [: C' e+ k* k
cursor.execute('create database '{}';'.format(uid))
" \0 A/ J! s/ R! J6 Y& l: ?$ K cursor.execute('use '{}';'.format(uid))
1 i7 g; w+ c4 m+ w3 @ msg = ''': E: R0 H/ s1 H# _
创建表 msg( q$ @+ M* L5 ], n! N
(
( S4 c* _: }' r7 ], l. f$ N: @ id text,
9 {( \3 s5 N) e, g$ v0 _ c) W# ] name char(100),# K, G. a, U7 Q" P, \3 R
content TEXT,
7 I. t1 b0 n3 u; P% q. R) \ createtime timestamp,
" \) M/ ]+ e: X3 P1 S tid char(32),
7 D K" W2 h; T4 X; S8 B* t) v location char(32),
6 a0 j$ f x G! M: W8 }! O posx int(20),% n6 M0 f" S! {' K s! I
posy int(20),
; q) ~) u8 K% a+ ] comment_num int(11),
0 w4 d$ v( j9 ^+ p# k. a like_num int(11),+ n0 t' C/ ?' f+ D7 B
pic_url TEXT,' U l, D- i6 q8 T
pic_num int,: W4 t- L. o% x& \
source_appidchar(32),
+ j1 D0 z5 q% D, w4 t source_name char(32),
N3 p, u. g; t is_tran char,& K1 \! {- c7 h }& Q n
trans_num char(32),
( Z5 M4 E, `: W) e$ J+ f! A: o0 [ trans_content TEXT4 s; A2 n1 X/ p1 L( A4 G1 `
);" P; N" M- `6 P( `& T B1 @
'''
; i o% l( g# u5 t8 { cursor.execute(msg)0 n1 d7 q' K3 ^; a7 [
cmt = '''/ x. a/ Y( Q! h' x
create table comment7 w: O8 t$ P9 [
(0 T% ]7 J* j' I$ a( {6 d4 H
tid char(32),+ L' \- \$ \$ [' P$ F. v
id text,- x4 Y: |/ {$ b
name char(100),$ j8 B, ^$ s* E% u6 a3 ^4 I1 k2 `
content TEXT," x0 F$ }( e4 T
createtime timestamp,
1 [2 `9 X K0 `1 r reply TEXT
; N a* j2 P: M )
) g$ L8 J4 i# ~5 O. p '''9 k4 B7 @. A# L. K
cursor.execute(cmt)
9 v; |* o3 T) N5 w like_table='''
- H ^2 g* H% H% S r" A create table like_table
& R8 A# p3 P; ` (
+ ~/ n6 _% M7 ]; C! R tid char(32),2 u3 W# c9 ]4 _( n
id text,
% R% {/ A6 j8 |6 q% u4 ~ name char(100),
% v. E. r6 C; `/ J" T s addr char(32),! V# `5 a! m! w0 X1 A
constellation char(32)、: Y5 l% [& |7 i2 r' b
gender char(4)、
: a6 s7 P5 V( A! F6 Mif_qq_friend int(1)、7 o# S6 E0 a+ J
if_special_care int(1)、
`! p2 L0 l! ois_special_vip int(1)、
; u( n& I8 h# V5 h, Mportrait TEXT
0 }' I; E2 H0 E: [$ u )% i; {! Q5 f, v1 j
'''3 o+ j+ T- ? P. s
cursor.execute(like_table)6 r! K& o# R1 K8 V
self.conn.commit()7 A) N* `; X+ c4 z6 [
return 1: B4 q" i: ?$ ?7 {
else:
! l0 V7 r- y2 X& J! \" r C+ u return 0
" S+ H: @; ~. P! q- L# K: P/ e
1 h4 d+ z1 m; N7 }& D7 K1 R- ~————————————————/ U8 r+ h$ p! U2 T
|
|