CSDN批量关注 python+golang+selenium
最近研究了下selenium,除了模拟鼠标点击,或者input操作外,还发现了个非常神奇的地方,可以执行JS代码。
这简直就像发现新大陆一样!如果能够这样的话,根本不用去考虑什么cookie,加密,认证等等!
于是想做下实验,于是我想到了csdn。。。(希望不要被封账号),通过selenium批量关注好友~~·哈哈哈
1. 首先我们当然是开始分析CSDN关注的时候网络请求是什么
随便找一个陌生人的主页:
https://me.csdn.net/weixin_41792682 (缘分选择,哥们见谅)
打开debug窗口,查看点击“关注”后执行的代码:
格式之后,发现了如下代码。
可以看出是通过ajax发送post请求,然后结合下network抓包的数据:
就能分析出来需要执行的简化版js代码如下:
$.ajax({
contentType: "application/json",
cache: false,
async: false,
type: "POST",
data:JSON.stringify({username: username}),
url: "https://me.csdn.net/api/relation/create",
success: function (data, status) {
console(data);
}
});
也就是如果需要关注某个用户,只需要将username传入data中就行了。那如何批量获得用户名呢?
2. 爬取用户名
csdn的用户名命名规则不统一,有些是昵称,有些是微信号,或者qq
比如:
https://me.csdn.net/weixin_42219106
https://me.csdn.net/qq_41893578
并不是id递增这么简单。最后发现,每个人的主页都会展示粉丝和关注,一共12个人。
查看网页源码:
从a标签中能提取出。
接下来的做法就是,从一个用户的主页开始,抓取出关注和粉丝的地址,接下来对这些关注和粉丝的用户递归抓取。就能抓取出源源不断的用户主页。
这一段通过python实现:
import requests
import bs4
import time
def getUsernamesFromUrl(user):
users=[]
s = requests.Session()
ret = s.get(user)
soup = bs4.BeautifulSoup(ret.content, "html.parser")
matches=soup.findAll(attrs={"class": "fans_title"})
for match in matches:
print(match["href"])
users.append(match["href"])
HistoryUsers.append(user)
return users
if __name__ == "__main__":
HistoryUsers=[]
PendingUsers=[]
startUser="https://me.csdn.net/dqcfkyqdxym3f8rb0"
PendingUsers.append(startUser)
count=0
while True:
time.sleep(1)
if len(PendingUsers)>0:
user=PendingUsers.pop()
users=getUsernamesFromUrl(user)
for u in users:
if u not in HistoryUsers:
print("new user:",u)
count+=1
index=count//100
filename="csdn/csdn_users{}.txt".format(index)
with open(filename,"a+") as f:
f.writelines(u.split("/")[-1:].pop()+"
")
PendingUsers.append(u)
else:
time.sleep(1)
最终会在csdn文件夹中生成多个文件,每个文件中有100个username
有了用户名,有了需要执行的代码,接下来的事情,就是从文件读取用户名,然后依次去执行。
3. go+selenium模拟执行js
最后到了最激动人心的地方。selenium的webdriver python有支持的库,golang也有,个人感觉go用起来更爽,因为有天生的goroute
用到的库:
"github.com/tebeka/selenium"
具体用法可参考GitHub,这里就不介绍了,可以模拟按键在input中输入用户名密码,点击登录按钮。
我就自己手动登录,登录成功后,按下回车,让程序往下跑:
func main() {
//自己封装了下selenium,只是为了方便使用,没有实质修改
wd, err := webApi.NewWeb("firefox", port)
if err != nil {
panic(err)
}
defer func() {
wd.Close()
}()
//打开登录页面
wd.Open("https://passport.csdn.net/account/login")
var url string
fmt.Println("手动登录")
//程序跑到这里会等待我输入,利用这个时间,我可以去手动登录
fmt.Scanln(&url)
wd.Open("https://me.csdn.net/weixin_43446306")
wd.GetDriver().SetImplicitWaitTimeout(time.Second * 5)
//两个goroute,1个去读usernames,另一个去执行js
go readNamesFromFile(filename)
go fetchUserNames(wd)
...
}
读取用户名:
func readNamesFromFile(file string) {
f, err := os.Open(file)
if err != nil {
log.Debug("err:$v", err)
return
}
buf := bufio.NewReader(f)
for {
timer := time.NewTicker(time.Second * 2)
<-timer.C
line, err := buf.ReadString("
")
line = strings.TrimSpace(line)
log.Debug("got name %s", line)
usernamesChan <- line
if err != nil {
if err == io.EOF {
log.Debug("end of file")
return
}
return
}
}
return
}
func fetchUserNames(wd *webApi.WebDriver) {
for {
username := <-usernamesChan
script := fmt.Sprintf(`function test(){
var ret="nok"; $.ajax({
contentType: "application/json",
cache: false,
async: false,
type: "POST",
data:JSON.stringify({username:"` + username + `"}),
url: "https://me.csdn.net/api/relation/create",
success: function (data, status) {
ret= "ok";
}
}); return ret}
return ( test());
`)
ret, _ := wd.GetDriver().ExecuteScript(script, nil);
if reflect.TypeOf(ret) != reflect.TypeOf("ok") {
return
}
if ret.(string) == "ok" {
beelog.Debug("follow user %s ok", username)
}
}
}
最终效果,在浏览器网络窗口能看到:
这种事情当然要开小号。
- 上一篇:没有了
- 下一篇:没有了