推特爬虫工具开发

这篇博客将从头到尾展示关于如何实现推特爬虫。
由于推特其设置的请求频率较低且反爬力度较高,无法使用Scrapy来实现较高效率的爬取,因此选择用Selenium作为爬取模块来进行爬虫主体的构建。

程序流程图

为方便理解,附上程序思路的流程图框架

启动浏览器

在Selenium模块里内置了关于chrome的驱动器,因此在使用selenium驱动chrome之前需要安装对应版本的chromedriver。度娘一下即可获得。
以下是程序使用时所需要到的模块,以及驱动chrome的代码,采用无头浏览器则不会弹出浏览器(可于程序调试完成后使用),调试程序以普通的浏览器模式进行,易观察到bug的位置。

from selenium import webdriver
import time
import re
import numpy as np
from bs4 import BeautifulSoup as bs
import pandas as pd
import os
import csv
import random
import traceback
def Chrome_activate():#此部分是驱动chrome浏览器,采用的是无界面形式,最小化内存空间option = webdriver.ChromeOptions()option.add_argument('headless')#设置为无头浏览器模式option.add_argument('--lang=zh-cn')#此句是为了让网页以中文的形式显示browser = webdriver.Chrome(chrome_options=option)# browser=webdriver.Chrome()return browser

在启动浏览器之后,键入要访问的用户id名,进入该用户主页。

input_name = input("请输入爬取用户的id:(如爬取用户id为@KingJames,则输入:KingJames)")
user_url='https://www.twitter.com/'+input_name
#得到要首先访问的用户网址

用户登陆及验证

推特在访问用户主页时,非登陆状态下无法进行粉丝页和关注页的跳转,因此需要解决的第一个部分是自动登陆及验证
下图为未登陆状态下访问推特用户主页的页面示意图:

可以看到在登入到用户页面后,右上角会出现输入用户名及密码的橙色方框,找到其对应的网页Html元素的位置,将用户名及密码输入到对应的方框里,最后点击登陆即可。
详细步骤如下:
(1)在程序路径下创建一个存放账号密码以及验证手机号的txt文本,按照(账号,密码,验证手机号)的格式,在程序启动后随机抽取一个账号进行登陆

file_ac = (os.path.realpath(__file__))[:-12]
#由于找到的是该程序文件,但只需进入到该文件夹,因此需将xxx.py略去
account_password = file_ac + '账号密码.txt'#找到存放账号密码的txt文件
file_account = open(account_password)
lines = file_account.readlines()
user_2 = {}#存放密码
user_3 = {}#存放验证手机号
for line in lines:#把账号密码放进对应的字典里user_list = line.split(',')account = user_list[0]password = user_list[1]callnumber = user_list[2][:-1]#将账号与密码及手机号以键值对的形式一一对应起来user_2[account] = passworduser_3[account] = callnumber

(2)随机抽取一个账号进行登陆

#随机选择一个账号进行登陆
login_account = random.sample(user_1.keys(), 1)
login_account = login_account[0]
login_password = user_2[login_account]
login_callnumber = user_3[login_account]
print("登陆账号为:{}".format(login_account))
print("登陆密码为:{}".format(login_password))#找到账号和密码的输入栏,将账号与密码输入至方框内
user_id = browser.find_element_by_xpath('//input[@class="text-input email-input js-signin-email"]')#找到方框对应的位置
if user_id:user_id.clear()#清楚掉方框里原有的内容user_id.send_keys("{}".format(login_account))#将账号传入
else:print("用户登录界面未显示")#可能存在账号密码方框未自动浮现的情况time.sleep(3)browser.close()
user_password = browser.find_element_by_xpath('//input[@class="text-input"]')
user_password.clear()
user_password.send_keys("{}".format(login_password))
#找到登陆的点击键
click = browser.find_element_by_xpath('//input[@class="EdgeButton EdgeButton--primary EdgeButton--medium submit js-submit"]')
click.click()
time.sleep(3)#需预留时间让网页进行加载。

(3)登陆完毕后,由于推特可能识别到IP地址的变化而转到验证手机号码的页面上,或者账号由于被识别到频繁请求而被禁止访问,因此需要对这两种情况进行识别,具体实现与登陆相同,即将验证手机号码键入即可。

if 'https://twitter.com/account/login_challenge' in browser.current_url:print("该账号需进行身份验证,正在进行中")call_number = browser.find_element_by_xpath('//input[@id="challenge_response"]')call_number.send_keys(login_callnumber)browser.find_element_by_xpath('//input[@id="email_challenge_submit"]').click()time.sleep(2)browser.get(url)time.sleep(2)
if 'https://twitter.com/account/access' in browser.current_url:print("该账号已被锁定,需要从存储文件中将其删除")break

用户主页的推文爬取

在进行爬取之前,需要根据我们自身的需求去锁定页面里对应元素的位置所在,下图为推特新版用户页面的示意图:
我们想要获得的是推文的发布者昵称、发布者id号、推文内容、发布时间、评论数、点赞数、转发数等。因此,需要在网页结构里找到这些元素的位置,实现爬取。
具体步骤如下:
(1)创建存储数据的文件

#首先是创建存储数据的文件
csv_path_1 = (file_path +'{}/'.format(input_name)+ '{}的推文数据.csv'.format(input_name))
with open(csv_path_1, 'w', newline='', encoding="utf_8_sig") as f:writer = csv.writer(f)head = ["用户id", "推文内容", "推文时间", "评论数", "转发数", "喜欢数", "链接"]writer.writerow(head)

(2)模拟浏览器的向下滚动:
由于推特采用的是异步加载数据的形式来显示网页,而相对应的存储异步数据的链接无法获取,因此只能在selenium中对其进行向下滚动的操作从而使得数据得到不断地加载,直到页面尽头。

old_scroll_height = 0 #表明页面在最上端
js1 = 'return document.body.scrollHeight'#获取页面高度的javascript语句
js2 = 'window.scrollTo(0, document.body.scrollHeight)'#将页面下拉的Javascript语句
while (browser.execute_script(js1) > old_scroll_height):#将当前页面高度与上一次的页面高度进行对比old_scroll_height = browser.execute_script(js1)#获取到当前页面高度browser.execute_script(js2)#操控浏览器进行下拉time.sleep(1.5)#空出加载的时间

通过一个while循环让浏览器执行javascript语句来不断下拉滚动,循环终止条件是页面高度不再发生变化。

(3)推文数据的爬取:
由于selenium模拟浏览器的过程中对内存的消耗过大,倘若等所有内容加载完毕后再进行数据的输出,可能在加载过程中便由于内存不足而出现页面崩溃的情况。
因此在推文数据的爬取过程中的规则是:每模拟下拉浏览器一次,便进行一次推文数据的输出,具体实现代码如下:

#由于转发、评论、点赞的爬取代码一样,故以函数方法表示
def count(crd):if "千" in crd.text:comment_number = int(float((crd.text)[:-1]) * 1000)  # 将带有千字的数据转化为整数elif "万" in crd.text:comment_number = int(float((crd.text)[:-1]) * 10000)  # 将带有万字的数据转化为整数else:  # 没有带千字或万字if crd.text:comment_number = int(crd.text)else:  # 若不存在数据,则设为0comment_number = 0return comment_numbercontent = bs(browser.page_source, 'html.parser')
#使用Beautifulsoup读取selenium获取到的网页内容
tw_user_1=content.find_all('article',re.compile('css-1dbjc4n r-1loqt21 r-1udh08x'))
#采用正则表达式re来实现用户推文的搜索,避免因为后续标签属性的改变而导致无法定位
print("已获取到{}条推文(可能有重复推文)".format(len(tw_user_1)))#获取用户发布的推文数据
user_content=content.find_all('article',re.compile('css-1dbjc4n r-1loqt21 r-1udh08x'))#获取到当前加载的所有推文数据
for i in range(len(user_content)):#遍历所有数据w_text=user_content[i].find('div',re.compile('r-a023e6 r-16dba41 r-ad9z0x r-bcqeeo r-bnwqim r-qvutc0'))#获取到当前推文的内容if w_text:#判断是否有推文内容,因为存在转推的情况tw_text=w_text.textelse:tw_text='该推文无文字内容(编号:{})'.format(m)m=m+1if tw_text not in tw_list:#可能存在同一推文被再次爬取,因此需要判断。tw_number = tw_number + 1tw_list.append(tw_text)tw_data['user']=user_content[i].find('div','css-1dbjc4n r-18u37iz r-1wbh5a2 r-1f6r7vd').text#获取推特用户idtw_data['text']=tw_text.rstrip()#获取评论数,转发数,点赞数crd=user_content[i].find_all('div','css-1dbjc4n r-1iusvr4 r-18u37iz r-16y2uox r-1h0z5md')#定位到评论数,点赞数,转发数所在的标签comment_number=count(crd[0])tw_data['comment'] = comment_numberreturn_number=count(crd[1])tw_data['return'] = return_numberlike_number=count(crd[2])tw_data['like'] = like_numbertw_data['time'] = user_content[i].find('time')['datetime'][:-5]#推文时间tw_data['href']= 'http://twitter.com/'+user_content[i].find('a','css-4rbku5 css-18t94o4 css-901oao r-1re7ezh r-1loqt21'' r-1q142lx r-1qd0xha r-a023e6 r-16dba41 r-ad9z0x r-bcqeeo r-3s2u2q r-qvutc0')['href']#将每一次循环爬取到的数据以追加的方式写入已经创建好的csv文件中tw_data.to_csv(file_path + '\\{}'.format(input_name) + '\\{}的推文数据.csv'.format(input_name), mode='a',header=False, index=False, encoding="utf_8_sig")else:continue

至此,完成对推特指定用户主页的推文爬取过程!

指定用户的粉丝关注爬取

接下来对指定用户的粉丝关注进行爬取。

需要获得的内容是用户的昵称,id,以及用户自我简介
具体步骤如下:
(1)创建存储数据的文件,与上一部分的代码无异,修改生成的文件名以及列名即可

def follower_crawl(panduan_2,browser,url,run_number,second_href_list=0):#跳转到关注者界面,开始进行爬取
#panduan_2:判断是否达到指定的爬取数量,达到则跳出循环
#browser:为selenium的浏览器
#run_number:为运行的次数,用来区分是否是已重启过的
#second_href_list:传入需爬取的用户链接列表if run_number==0:#一旦出现异常后,无需再重新创建新的文件,也无需进行关注用户的数目的重新爬取csv_path = (file_path + '{}/'.format(input_name)+'{}的关注者.csv'.format(input_name))with open(csv_path, 'w', newline='', encoding="utf_8_sig") as f:writer = csv.writer(f)head = ["用户id","用户说明"]writer.writerow(head)

(2)获得该用户的粉丝

     url=url+'/followers'#在原本用户主页的尾部加上/followers进入关注者界面browser.get(url)#从主页跳转到关注者的页面time.sleep(3)global name_list#定义全局变量,用来记录当下总共爬取到的粉丝数量,可用于指定粉丝数量后停止循环name_list=0old_scroll_height = 0href_list=[]namelist=[]#用于判断用户是否已经被爬取,实现去重的功能while(browser.execute_script(js1) > old_scroll_height):#模拟浏览器进行滚动下拉if name_list>=int(panduan_2):#判断是否达到指定的爬取数量,达到则跳出循环print("{}名关注者爬取完毕".format(str(panduan_2)))breakold_scroll_height = browser.execute_script(js1)browser.execute_script(js2)time.sleep(2.5)content=bs(browser.page_source,'html.parser')#解析网页all_followers=content.find_all('div','css-1dbjc4n r-1iusvr4 r-46vdb2 r-1777fci r-5f2r5o r-bcqeeo')#找到所有用户数据user_data=pd.DataFrame(data=np.zeros(shape=(1,2)),columns=['id','introduction'])#创建存储数据的Dataframeid_text=i.find('div','css-901oao css-bfa6kz r-1re7ezh r-18u37iz r-1qd0xha'' r-a023e6 r-16dba41 r-ad9z0x r-bcqeeo r-qvutc0')if id_text:#存在用户将主页锁定的情况,导致无法获取到id,因此需要进行判断id_text_i=id_text.textif id_text.text not in namelist:#实现去重的判断语句user_data['id'] = id_text_iname_list = name_list + 1  # 统计当下爬取到的用户数量introduction = i.find('div', 'css-901oao r-hkyrab r-1qd0xha r-a023e6 r-16dba41'' r-ad9z0x r-bcqeeo r-glunga r-1jeg54m r-qvutc0')if introduction:  # 爬取用户简介user_data['introduction'] = introduction.textelse:user_data['introduction'] = ''#将数据导出user_data.to_csv(file_path + '\\{}'.format(input_name) + "\\{}的关注者.csv".format(input_name),mode='a',header=False, index=False, encoding="utf_8_sig")href_list.append('http://twitter.com/' + id_text_i[1:])#将用户主页统计起来,用于下一步关于用户的关注爬取namelist.append(id_text_i)#将已经爬取好的用户加入到列表中else:continue

(3)在获取到指定数量的粉丝后,对每个粉丝的关注页面也进行爬取,得到其关注喜好

同样是创建对应的文件进行存储
#爬取关注者的信息,即用户的关注喜好if run_number==0:#若是第一次,则需要创建文件second_csv_path=(file_path+ '{}/'.format(input_name)+'{}关注者的信息.csv'.format(input_name))with open(second_csv_path, 'w',newline='',encoding="utf_8_sig") as f:writer = csv.writer(f)head = ["{}的关注者id".format(input_name),"该关注者关注的其他用户id",]writer.writerow(head)对每个粉丝进行单独的爬取,以下部分在后续实现程序重复运行时可以定义为一个函数体,使过程更为简洁清晰for i in range(len(href_list)):#循环遍历粉丝列表print("开始爬取第{}个关注者的关注信息".format(str(i+1)))second_name_list = []browser.get(href_list[i])#进入该粉丝的主页time.sleep(2)fnumber=browser.find_elements_by_xpath('//a[@class="css-4rbku5 css-18t94o4 css-901oao r-hkyrab'' r-1loqt21 r-1qd0xha r-a023e6 r-16dba41 r-ad9z0x ''r-bcqeeo r-qvutc0"]')#获取到该粉丝的关注量,超500的不予爬取,可能为僵尸推特,无实际爬取意义if fnumber:if fnumber[0].text:following_number = fnumber[0].get_attribute('title')print("该用户关注量为:{}".format(following_number))else:following_number = 0print("该用户关注量为:{}".format(following_number))else:print("该用户的Id已更新,无法访问")continueif len(following_number)>=4 or int(following_number)>500:#len(...)>4是因为可能存在关注为0的情况,页面上不会出现关注字样print("用户关注数量超过500,不进行爬取")continueelse:second_head_name = browser.find_element_by_xpath('//div[@class="css-901oao css-bfa6kz r-1re7ezh r-18u37iz'' r-1qd0xha r-a023e6 r-16dba41 r-ad9z0x r-bcqeeo r-qvutc0"]').textsecond_data = pd.DataFrame(data=np.zeros(shape=(1, 2)), columns=['head_name', 'id'])second_data['head_name'] = second_head_namesecond_data['id'] = input_namesecond_name_list.append(input_name)second_data.to_csv(file_path + '\\{}'.format(input_name) + "\\{}关注者的信息.csv".format(input_name),mode='a', header=False, index=False, encoding="utf_8_sig")#获取到该粉丝的idurl=href_list[i]+'/following'browser.get(url)#进入该粉丝的关注页面time.sleep(2).........注:接下来的程序部分也是实现滚动下拉去获得该粉丝所有的关注用户,因此代码与前面无异。因此在这儿不进行重复说明

至此,爬虫功能的实现部分已基本完成~

程序的异常重启

但由于使用selenium的过程中容易受到其他因素的干扰,如页面崩溃、网络状态不佳、内存不足的情况,会导致程序的突然中断,无法达到爬取要求。程序需要在遇到异常的时候从未完成的爬取位置重新开始爬取。
具体实现方式是借助在Try…Except语句的基础上加多一个while循环,实现异常后的重处理。代码如下:

由于报错位置基本出现在爬取粉丝喜好的过程中,因此异常处理不针对爬取推文的功能
while True:try:#由于崩溃报错的位置基本出现在爬取粉丝关注喜好的时候,因此爬取推文部分未放进此循环中if panduan_1=='y':if run_number==0:#第一次正常爬取follower_crawl(panduan_2,browser,url,run_number)#第一次爬取无需更改其他参数。elif run_number>0:#产生异常后refile=open(file_path + '\\{}'.format(input_name) + "\\{}关注者的信息.csv".format(input_name),encoding="utf_8_sig")#redata=pd.read_csv(refile)#读取已爬到的数据last_user=redata.iloc[-1,0]#锁定上次报错时的爬取位置print("中断前爬取的用户为:{}".format(last_user))#找到最后一个用户id#将未进行爬取的用户id写进一个列表中renew_f = open(file_path + '\\{}'.format(input_name) + "\\{}的关注者.csv".format(input_name),encoding="utf_8_sig")renew_data = pd.read_csv(renew_f)for i in range(len(renew_data)):#搜索最后一个已爬取用户的位置if last_user == renew_data['用户id'][i]:renew_href = renew_data.iloc[i:,0]#得到未进行爬取的用户idelse:continuesecond_href_list = []#重新构造出一个新的用户链接列表renew_href=renew_href.reset_index(drop=True)for i in range(len(renew_href)):second_href = 'https://twitter.com/' + renew_href[i][1:]second_href_list.append(second_href)#将未爬取的用户链接加入到列表里follower_crawl(panduan_2,browser,url,run_number,last_user,second_href_list)#再次调用粉丝爬取函数except Exception as e:#若产生异常run_number=run_number+1 #用于判断是否是异常后的再次爬取请求print("产生异常,重新爬取,异常原因为:{}".format(traceback.print_exc()))#打印出报错位置,原因browser.close()#关闭浏览器browser = Chrome_activate()#重新打开浏览器time.sleep(2)continueelse:#当程序满足指定条件后退出。print("程序运行正常,结束爬取")break

以上即为推特指定用户及其粉丝、粉丝的关注喜好爬取功能的实现代码。

程序工具化

为方便后续直接使用,将其封装为一个exe文件,但未进行操作界面的优化。封装时使用的是Pyinstaller模块。

Twitter推特爬虫工具开发相关推荐

  1. 记fofa爬虫工具开发篇

    记fofa爬虫工具开发篇 前沿 开发这个工具的原因单纯是在 大一(目前已经大三了)的时候 ,在用fofa搜索时,只能 搜索到 前 5 页,而且 github 上也找不到一个好点的爬虫,全是需要 会员的 ...

  2. 从0开始学爬虫6比价工具开发1之爬取当当、京东的数据

    从0开始学爬虫6比价工具开发1之爬取当当.京东的数据 爬取当当数据 spider_dangdang.py #coding=utf-8import requests from lxml import h ...

  3. 2020年最佳的5种社交媒体的数据抓取/网络爬虫工具

    2020年最佳的5种社交媒体的数据抓取/网络爬虫工具 查看原文章: 2020年最佳的5种社交媒体的网络爬虫工具 社交媒体数据抓取工具通常是指一种自动化网络爬虫工具,可从社交媒体渠道提取数据.它不仅包括 ...

  4. 【谷歌推网页爬虫新标准,开源robots.txt解析器】

    https://www.toutiao.com/a1638031116246019 [谷歌推网页爬虫新标准,开源robots.txt解析器] 对于接触过网络爬虫的人来说 robots.txt 绝不陌生 ...

  5. 暑期实训项目(2)--推特爬虫数据处理

    后端数据爬虫--Twitter 1. 爬虫的目标 2. 爬虫的思路 2.1 步骤 2.2 几个选择 2.2.1 Scarpy+ Selenium 2.2.2 没有使用Twitter API 3. 爬虫 ...

  6. 爬虫大全,爬虫工具汇总

    开源爬虫 开发语言 软件名称 软件介绍 许可证 Java Arachnid 微型爬虫框架,含有一个小型 HTML 解析器.是一个基于Java的web spider框架.它包含一个简单的HTML剖析器能 ...

  7. 爬虫python需要什么软件-python大神们!都在用什么爬虫工具呢?

    python大神们!都在用什么爬虫工具呢? Python开发爬虫常用的工具总结 reqeusts:Python HTTP网络请求库; pyquery: Python HTML DOM结构解析库,采用类 ...

  8. 如何高效学Python?好用的爬虫工具又有哪些?

    作为当下最火的编程语言,Python 的受欢迎程度不用我多说.学好 Python ,不仅可以进阶资深 Python 工程师,去大厂,还能转型数据分析.人工智能等行业. 但与此同时,我也经常在后台看到一 ...

  9. 排名前20的网页爬虫工具,超多干货

    网络爬虫在许多领域都有广泛的应用,它的目标是从网站获取新的数据,并加以存储以方便访问.而网络爬虫工具越来越为人们所熟知,因为它能简化并自动化整个爬虫过程,使每个人都可以轻松访问网络数据资源. 1. O ...

最新文章

  1. python【蓝桥杯vip练习题库】ADV-298 和谐宿舍2(DP)
  2. 商丘网络推广中网站内部优化需要注意的要点有哪些?
  3. python乘法口诀表-用python打印99乘法口诀表
  4. 3Animation动画的创建,CCSpeed,CCFollow
  5. scp报错:not a regular file
  6. 自然语言理解——introduction
  7. 判断输入的整数是否为素数_C语言 | 判断是否素数
  8. LINQ学习中需要明确的几点问题
  9. 段错误(已转储)什么意思_发表论文已检索什么意思
  10. pandas 删除特定行根据条件_记录21个Pandas技巧
  11. (转)趋势因子:利用投资期内所有信息的获利方法
  12. 图像文字识别易语言代码
  13. 查成语--每天10行python代码系列!
  14. 单链表---建立基本学生信息管理系统
  15. 魔兽争霸修改器,局域网内使用!防封号!!!!!!!!
  16. Android arm64-v8a、armeabi-v7a、armeabi、x86详解
  17. 最低报酬率计算机公式,股票报酬率计算公式
  18. 5w2h原则指的是什么_5W2H指的是什么?
  19. 8、两种典型微处理器介绍
  20. Java趣味编程(一)

热门文章

  1. TensorFlow之文本分类算法-2
  2. Solr - DIH详解(上卷)
  3. C++/C++11中头文件sstream介绍
  4. (转)execute、executeQuery和executeUpdate之间的区别
  5. Summary - 文件导出测试
  6. 驱动之家驱动界定说明
  7. linux操作mysql数据库
  8. 4kw机柜无通道封闭CFD模拟分析及优化(下篇) -孙长青
  9. Node.js 15正式版发布
  10. DDS、openDDS和fast DDS介绍