想要了解一个人,可以从ta的微博开始下手,微博的关注列表可以很好地看出一个人的兴趣。实验计划获取目标微博账号的关注列表并实现批量关注。
为减少网页反爬策略对实验产生影响,选取手机端网页进行分析(m.weibo.com)。下面根据关注的三个步骤进行分析。
打不开手机端网页的情况:
输入网址后按F12打开开发者工具,在依次点击下方两个位置选择手机模式后即可正常浏览。
查看网页源代码,发现关注列表等信息明显是用js渲染出来的,很难通过requests直接获取,因此选择Selenium库作为爬取工具。
在进行一切操作之前需要登录自己的账号,而登录过程包含各种验证手段,不方便处理。因此在爬取页面前手动登录账号,并在chrome-开发者工具-Application-cookies中获取相应的cookie。在访问时,手动添加cookie可以保持登录的状态。
发现有差异的部分仅为“followers_-_”之后,“_-_1042015”之前的部分(经验证知该值为微博用户号),postman检验知“_-_1042015”及其之后的参数均可去除。
由此得到了url格式:
url = 'https://m.weibo.cn/p/index?containerid=231051_-_followers_-_{}'.format(targetid)
# targetid 为目标账户的id
页面每次向下滚动时会产生新的用户。为保证不会因操作频率过快而被目标网站检验,每次下拉都需要设置一定时间间隔。
右键点击关注按钮,即可在开发者工具中查看按钮在html中的位置。
我们很快地找到按钮的位置,每一个class=“card m-panel card28 m-avatar-box”的div下“class="m-font m-font-follow"的i元素,据此可以很快使用xpath定位。但Selenium的click函数默认不支持对i元素的点击,采用execute_script函数执行js脚本可以绕过这个限制。
follow_button = browser.find_element_by_xpath('.//i[@class="m-font m-font-follow"]')
browser.execute_script("arguments[0].click();", follow_button)
time.sleep(random.uniform(0,1))
与下拉一样,为防止被目标网站屏蔽,每次点击设置一定时间间隔。
注:
运行前提前安装Selenium库,以及相应的webdriver。
pip install selenium
根据自己的chrome版本下载chromediver并添加至环境变量。
from selenium import webdriver
import random
import time
total_num = 50
# 使用前手动获取cookies
cookies = [{"name": "_T_WM", "value": "85458941036"},
{"name": "SUB", "value": "_2A25MpEV9DeRhGeFN6VAU9i7OwzSIHXVsZ2s1rDV6PUJbkdAKLXn9kW1NQEE7SA2lhJKFE8uT2XTdS1cBtmzNj532"},
{"name": "WEIBOCN_FROM", "value": "1110006030"},
{"name": "MLOGIN", "value": "1"},
{"name": "XSRF-TOKEN", "value": "b96148"},
{"name": "M_WEIBOCN_PARAMS", "value": "luicode%3D10000011%26lfid%3D231051_-_followers_-_1350995007_-_1042015%253AtagCategory_050%26fid%3D231051_-_followers_-_1350995007_-_1042015%253AtagCategory_050%26uicode%3D10000011"},
#{"name": "loginScene", "value":"102003"}
]
# div class="m-dialog"
# 滚动至最底端
def roll(driver):
# 获取页面初始高度
js = "return action=document.body.scrollHeight"
height = driver.execute_script(js)
# 将滚动条调整至页面底部
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
# 初始时间
t1 = int(time.time())
# 循环标识
status = True
# 重试次数
num = 0
result = []
while status:
# 判断数量是否足够
result = driver.find_elements_by_xpath('//i[@class="m-font m-font-follow"]')
count = len(result)
if count >= total_num:
print("已达到预期数目.")
status = False
# 当前时间
t2 = int(time.time())
time.sleep(1)
# 判断时间初始时间戳和当前时间戳相差是否大于5秒,小于5秒则下拉滚动条
if t2 - t1 < 5:
new_height = driver.execute_script(js)
if new_height > height:
time.sleep(random.uniform(1,2))
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
# 重置初始页面高度
height = new_height
# 重置初始时间戳,重新计时
t1 = int(time.time())
elif num < 3: # 当超过5秒页面高度仍然没有更新时,进入重试逻辑,重试3次,每次等待3秒
time.sleep(3)
num = num + 1
else: # 超时并超过重试次数,程序结束跳出循环,并认为页面已经加载完毕!
print("滚动条已经处于页面最下方")
status = False
# 滚动条调整至页面顶部
driver.execute_script('window.scrollTo(0, 0)')
break
print("共{}个用户可供关注。".format(count))
def main():
start = time.time()
# 开发者模式
options = webdriver.ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable - automation'])
# 启动浏览器
browser = webdriver.Chrome()
# 目标用户的id
targetid = 1827683445
url = 'https://m.weibo.cn/p/index?containerid=231051_-_followers_-_{}'.format(targetid)
browser.get(url)
time.sleep(2)
# 添加cookie
for cookie in cookies:
browser.add_cookie(cookie)
# 刷新状态
browser.refresh()
# 下滚并获取目标数量的用户
roll(browser)
# 查找用户所在div
targets = browser.find_elements_by_xpath('//div[@class="card m-panel card28 m-avatar-box"]')
if len(targets) != 0:
count = 0
for target in targets:
# 查找用户昵称
name = target.find_elements_by_xpath('.//h3')[0].text
# 查找关注按钮
follow_button = target.find_elements_by_xpath('.//i[@class="m-font m-font-follow"]')
if len(follow_button) != 0:
follow_button = follow_button[0]
# 检查是否有弹窗
dialog = browser.find_elements_by_xpath('//div[@class="m-dialog"]')
if len(dialog) == 0:
print("Success: 成功关注 {}".format(name))
# 保存至文件
with open('followname_{}.txt'.format(targetid), 'a', encoding='utf-8') as f:
f.write("%s\n" % name)
# 点击关注
browser.execute_script("arguments[0].click();", follow_button)
time.sleep(random.uniform(0.6,1.5))
count += 1
else:
print("Error: 关注失败.")
break
if count == total_num:
break
print("共关注了{}个用户.".format(count))
print("耗费时间:{}s".format(time.time()-start))
time.sleep(10)
if __name__ == '__main__':
main()
整体算是实现了基本的自动化功能,关注效率偏低。毕竟每次关注都会向目标网站发送请求,请求到达一定频率账号就会被封禁(我的账号昨天封了一整天。。。)。如果代理ip的话,由于每次发送关注请求的均为同一个账户,还是无法避免账户的封禁。
另外还写了一个批量取消关注的程序,原理类似:
from selenium import webdriver
import random
import time
# 使用前手动获取cookies
cookies = [{"name": "_T_WM", "value": "85458941036"},
{"name": "SUB", "value": "_2A25MpEV9DeRhGeFN6VAU9i7OwzSIHXVsZ2s1rDV6PUJbkdAKLXn9kW1NQEE7SA2lhJKFE8uT2XTdS1cBtmzNj532"},
{"name": "WEIBOCN_FROM", "value": "1110006030"},
{"name": "MLOGIN", "value": "1"},
{"name": "XSRF-TOKEN", "value": "b96148"},
{"name": "M_WEIBOCN_PARAMS", "value": "luicode%3D10000011%26lfid%3D231051_-_followers_-_1827683445_-_1042015%253AtagCategory_050%26fid%3D231051_-_followers_-_1827683445_-_1042015%253AtagCategory_050%26uicode%3D10000011"}
]
# 滚动至最底端
def roll(driver):
# 获取页面初始高度
js = "return action=document.body.scrollHeight"
height = driver.execute_script(js)
# 将滚动条调整至页面底部
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
time.sleep(5)
# 定义初始时间戳(秒)
t1 = int(time.time())
# 定义循环标识,用于终止while循环
status = True
# 重试次数
num = 0
while status:
# 获取当前时间戳(秒)
t2 = int(time.time())
# 判断时间初始时间戳和当前时间戳相差是否大于5秒,小于5秒则下拉滚动条
if t2 - t1 < 5:
new_height = driver.execute_script(js)
if new_height > height:
time.sleep(1)
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
# 重置初始页面高度
height = new_height
# 重置初始时间戳,重新计时
t1 = int(time.time())
elif num < 3: # 当超过5秒页面高度仍然没有更新时,进入重试逻辑,重试3次,每次等待3秒
time.sleep(3)
num = num + 1
else: # 超时并超过重试次数,程序结束跳出循环,并认为页面已经加载完毕!
print("滚动条已经处于页面最下方!")
status = False
# 滚动条调整至页面顶部
driver.execute_script('window.scrollTo(0, 0)')
break
def main():
total = 20
count = 0
browser = webdriver.Chrome()
browser.get('https://m.weibo.cn/p/index?containerid=231093_-_selffollowed')
for cookie in cookies:
browser.add_cookie(cookie)
browser.refresh()
roll(browser)
follows = browser.find_elements_by_xpath("//i[@class=\'m-font m-font-followed\']")
for follow in follows:
# 取消关注
follow.click()
time.sleep(random.uniform(0.5, 1.0))
# 确定按钮
browser.find_element_by_xpath("//a[@class=\"m-btn m-btn-white m-btn-text-orange\"]").click()
count += 1
if count == total:
break
print("Done")
time.sleep(10)
if __name__ == '__main__':
main()
1.Selenium实现所见即所得,可以有效实现对js渲染页面元素的爬取。
2.一般的网站都需要登录,传入cookie可以保持登录状态。
3.爬取过程中会遇到被目标网站封禁的情况,需要灵活调整等待时间以最大程度模拟人的行为。
1.Selenium实现所见即所得,可以有效实现对js渲染页面元素的爬取。
2.一般的网站都需要登录,传入cookie可以保持登录状态。
3.爬取过程中会遇到被目标网站封禁的情况,需要灵活调整等待时间以最大程度模拟人的行为。
4.目前实现效率不是很高,由于每次关注都会向目标网站发送请求,请求到达一定频率就会被封禁(大概关注30个用户就会被封禁了)。暂时没有找到合理的解决方法。求大佬提供思路。如果只是爬取信息的话被封禁的概率则要低一些。