700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > python爬网络图片脚步_python爬虫入门 实战(五)---用webdriver实现批量自动发微博...

python爬网络图片脚步_python爬虫入门 实战(五)---用webdriver实现批量自动发微博...

时间:2020-03-04 09:51:00

相关推荐

python爬网络图片脚步_python爬虫入门 实战(五)---用webdriver实现批量自动发微博...

效果展示动图

需求:

博主之前有一段时间突然不想玩微博了,然后正好表弟想玩,就给他用了,手机绑定也换成了他的号码。近期突然又想要玩,就重新开了个号。新号微博空空的,也没有什么关注。于是就产生了两个需求,正好可以借这个机会学习一下自动化测试工具webdriver的基本使用:

1、将原微博的博文搬到新账号

2、用新账号关注原微博的所有关注

涉及:

1、request的基本使用

2、json解析

3、用request下载图片并保存到本地

4、用webdriver登录并发送微博

5、用autoIT解决非input型的文件上传

本篇目录:

1.思路

2.爬取原微博

3.用webdriver启动浏览器登录微博

4.用webdriver发布带图片的微博

5.完整代码

6.效果展示动图

7.参考

图1.封面而已

思路

1、爬取原微博的博文内容,爬一条发一条

2、文字信息保存在list里,图片保存到本地

3、用webdriver实现批量发送微博

4、上传图片借助第三方工具autoIT

注:

为什么用webdriver来发微博而不用微博开放平台的api?

因为发布带图片的微博的api是高级api需要创建应用并通过审核才能申请,而过审并不是可以糊弄的,需要成熟且系统的应用。

爬取原微博

这里会直接引入实战四的代码模块crawl_weibo,其中的getWeibo方法可以获取到指定微博用户的所有微博的json,并提取其中的cards(一个list数组,所有的微博信息都在里边)返回,该模块详细源码参见以上链接末尾。

getWeibo方法:

# 获取指定博主的所有微博card的list

def getWeibo(self,id,page):#id(字符串类型):博主的用户id,page(整型):微博翻页参数

url='/api/container/getIndex?type=uid&value='+id+'&containerid=107603'+id+'&page='+str(page)

response=requests.get(url)

ob_json=json.loads(response.text)

list_cards=ob_json['cards']

return list_cards# 返回本页所有的cards

首先明确我们要爬取原微博的所有除了转发以外的微博的原文、图片以及来源、点赞数、评论数等其他信息。接下来先分析一下json 的结构,json的格式化推荐。

图2.json结构

之前教程里也说了,json就是list和dict的互相嵌套,所以只要会用list和dict,搞清楚json结构,提取数据就很容易了。

我们把文字信息提取出来,把图片都下载到本地的一个文件夹里,以便之后发微博上传。对于图片存储,用request来get一下图片的url,获取的返回内容用open创建一个.jpg格式的file来写入。为了上传的时候方便填写图片地址,图片按顺序编号存入项目下的img文件夹中。上传时只需要根据图片总数按序遍历即可

获取微博内容数据代码:

def getContent(self,card):# 获取该card下微博的内容

mblog=card['mblog']

count_img=0# 每条微博重新计数图片数,用于发送图片的地址

text= html.fromstring(mblog['text'])

text=text.xpath('string(.)')#过滤正文中的多余标签

url_img=''

if 'pics' in mblog:#如果mblog中有键'pics',说明该条微博有图片,存储图片到本地

pics=mblog['pics']

for pic in pics:

url_img=pic['large']['url']

ir = requests.get(url_img)

if ir.status_code == 200:# 如果请求已成功

count_img+=1# 给图片计数

open('../img/'+str(count_img)+'.jpg', 'wb').write(ir.content)# 保存在img文件夹下,发送也从这个文件夹下找

scheme=card['scheme']# 原微博链接

created_at=mblog['created_at']# 原微博创建时间

source=mblog['source']# 原微博来源,即终端

reposts_count=mblog['reposts_count']# 原微博转发数

comments_count=mblog['comments_count']# 原微博评论数

attitudes_count=mblog['attitudes_count']# 原微博点赞数

return text,count_img,scheme,created_at,source,reposts_count,comments_count,attitudes_count# 其实返回的是一个list

将getWeibo返回的list(也就是json中的cards)中的一个card传入以上函数,即可返回我们需要的数据,细节见注释。通过这个方法我们需要发布的微博数据已经可以爬取到了,接下来我们要登录微博把它们发布出去。

用webdriver启动浏览器登录微博

webdriver的使用其实非常简单。可以这样描述:

1、用驱动器启动浏览器并打开一个连接

2、根据页面html源码的结构找到指定的元素(按钮、文本框等)

3、对目标元素采取目标动作(点击、输入等)

使用webdriver需要下载selenium模块和浏览器驱动。

1、下载安装selenium模块,命令行:

pip install selenium

2、博主用的是chrome浏览器,所以本篇以其对应的驱动chromedriver.exe为例,百度或者google就可以找到下载链接,下载好了解压到任意路径即可,代码中指定该路径。

3、一定要保证selenium模块、浏览器驱动、浏览器都是最新版本,以免产生不必要的bug。一般pip指令安装的都是最新版本的模块。

做好以上准备之后,就可以在代码里引入webdriver了,我们先用登录来打个样儿。代码要做的就是驱动启动浏览器后,get微博的主页,用driver的各种find_element方法找到账号和密码输入框,键入账号密码进行登录,有时可能需要验证。以下展示的是账号输入框的位置与属性:

图3.账号输入框位置

webdriver的基本使用:

1、可以根据class和id来找到元素,也可以用xpath语法和css选择器来找到元素

driver.find_element_by_id('id')

driver.find_element_by_class_name('classname')

driver.find_element_by_css_selector('input[type="password"]')

driver.find_element_by_xpath('//div[@node-type="textElDiv"]/textarea[@class="W_input"]')

2、用element.click点击元素,用element.send_keys向文本框键入文字,其他动作可以用ActionChains,参考selenium之 玩转鼠标键盘操作(ActionChains)。

3、详细说明可参考Python爬虫利器五之Selenium的用法,更多用法参考网络教程和文档。

登录代码:

driver = webdriver.Chrome(executable_path='../drivers/chromedriver.exe')# chrome浏览器驱动

url='/'

driver.get(url)# get打开微博主页

input_account=driver.find_element_by_id('loginname')# 找到账号输入框

input_psw=driver.find_element_by_css_selector('input[type="password"]')# 找到密码输入框

#输入账号密码并登录

input_account.send_keys('account')

input_psw.send_keys('password')

bt_logoin=driver.find_element_by_class_name('login_btn')# 找到登录按钮

bt_logoin.click()# 点击登录

不过以上代码在网络状态不好的时候会产生找不到元素的bug,这主要是因为页面还没有加载完毕造成的。

应该将find账号输入框的代码修改为:

# 参数10为超时时间,即最大等待时间为10秒,关于lambda就自行百度吧

bt_logoin=WebDriverWait(driver,10).until(lambda x:x.find_element_by_id('loginname'))

出现验证码的情况:

在点击了登录按钮以后,有时会出现验证码。对于这种情况,我们只需要加入一个判断,如果点击登录后出现了验证码输入框,就断定需要输入验证码,我们在控制台手动输入并send_keys到验证码输入框即可。(验证码自动破解比较复杂,不建议在入门阶段研究)

处理验证码的代码:

# 根据验证框的存在与否判断是否要输入验证码

def isVerifyCodeExist(self):

try:# 如果成功找到验证码输入框返回true

self.driver.find_element_by_css_selector('input[name="verifycode"]')

return True

except:# 如果异常返回false

return False

# 输入验证码部分,如果无需输入则直接返回,否则手动输入成功后返回

def inputVerifyCode(self):

input_verifycode=self.driver.find_element_by_css_selector('input[name="verifycode"]')# 验证码输入框

bt_change=self.driver.find_element_by_css_selector('img[action-type="btn_change_verifycode"]')# 验证码图片,点击切换

bt_logoin=self.driver.find_element_by_class_name('login_btn')# 登录按钮

while self.isVerifyCodeExist():# 如果验证码输入框一直存在,则一直循环

print u'请输入验证码……(输入"c"切换验证码图片)'

verifycode=raw_input()

if verifycode=='c':

bt_change.click()

else:

input_verifycode.send_keys(verifycode)

bt_logoin.click()

# 点击完登录以后判断是否成功

if self.driver.current_url.split('/')[-1]=='home':# 如果连接已经跳转到home

print u'登录成功'

break

else:

print u'输入的验证码不正确'

在之前的登录代码末尾调用inputVerifyCode方法即可。

用webdriver发布带图片的微博

如果仅发布文字内容的微博,比较简单,思路与以上同理,先找到文本框元素,用send_keys输入内容,找到'发布'按钮,点击按钮即可发布。

# 上传文字

def upload_txt(self,text):

# 用xpath语法找到输入框

input_w=self.driver.find_element_by_xpath('//div[@node-type="textElDiv"]/textarea[@class="W_input"]')

input_w.send_keys(text)

# 发布

def send(self):

# 找到发布按钮并点击

self.driver.find_element_by_class_name('W_btn_a').click()

而对于需要发布图片的博文,我们还需要点击图片->点击单图/多图。接着我们可以发现目标对象不是input型的文件上传按钮,input型的文件上传我们可以用send_keys直接键入文件地址,方便快捷。但是微博这个文件上传是object型的,这只能借助第三方工具autoIT来对os弹窗进行一波骚操作了。

图4.图片上传按钮

从上图看,可以发现这个object的id包括'swf_upbtn'这样一个字段,之后很明显是一个随时可能变化的数字串,所以我们可以用xpath的contains函数来找到它,点击这个按钮以后会弹出上传文件的窗口,这个窗口就不是selenium能控制的了,需要编写autoIT脚本来控制文件的上传操作,再在python代码中通过os模块调用这个脚本。

我的autoIT脚本代码:

WinWait("打开","",10000);

ControlFocus("打开", "", "Edit1");

ControlSetText("打开" ,"", "Edit1", $CmdLine[1]);

Sleep(1000)

ControlClick("打开", "","Button1");

上述代码进行的操作是:

1、等待文件上传窗口的出现,最多等待10s(因为chrome浏览器的文件上传窗口标题是"打开",第一个参数是窗口标题)

2、将光标聚焦于文件地址编辑框

3、在编辑框里键入参数(也就是我们之后会传入的地址)

4、点击"打开"按钮

详细用法和说明请参阅以上两篇。

$CmdLine[1]可以读取到命令行的第一个参数,通过这个参数我们就可以把文件地址传入。关于参数的说明和其他的实现文件上传的方法参考Python Selenium —— 文件上传、下载,其实很简单。

上传文字和图片的方法封装如下:

# 上传文字

def upload_txt(self,text):

input_w=self.driver.find_element_by_xpath('//div[@node-type="textElDiv"]/textarea[@class="W_input"]')

input_w.send_keys(text)

sleep(1)

#运行上传图片脚步

def upload_img_script(self,time_bef,time_after,path):# path参数需要前后带双引号

sleep(time_bef)# 等待弹窗时间

os.system('C:/Users/15850/Documents/GitHub/MyWorkspace/py_study/script/upload.exe '+path)

sleep(time_after)# 等待图片加载时间

# 上传文字和单图

def upload_txt_img(self,text,img_path):

self.upload_txt(text)# 将文字上传

img=self.driver.find_element_by_css_selector('a[action-type="multiimage"]')# 图片按钮

img.click()# 点击图片按钮

sleep(1)# 等待加载其他按钮

#单图/多图按钮,即上传图片按钮

bt_uploadimg=WebDriverWait(self.driver,10).until(lambda x:x.find_element_by_xpath('//object[contains(@id,"swf_upbtn")]'))

bt_uploadimg.click()# 点击上传按钮

self.upload_img_script(1,2,img_path)

# 上传文字和多图

def upload_txt_multiImg(self,text,img_path_list):

self.upload_txt_img(text,img_path_list[0])# 将文字和第一张图片上传

len_imgs=len(img_path_list)# 图片地址list的长度

bt_uploadimg=WebDriverWait(self.driver,10).until(lambda x:x.find_element_by_xpath('//li[@node-type="uploadBtn"]/div/object[contains(@id,"swf_upbtn")]'))

for i in range(len_imgs-1):# 将剩余图片上传

bt_uploadimg.click()

self.upload_img_script(1, 2,img_path_list[i+1])

其中这一行即是调用了编译成exe文件的上传文件的脚步,参数字符串先指明脚本路径,空一格加上path这个参数。

os.system('C:/Users/15850/Documents/GitHub/MyWorkspace/py_study/script/upload.exe '+path)

注意path传入的字符串必须带双引号,字符串本身也需要单引号包括,且分隔符必须为"\",如下:

'"C:\\Users\\15850\\Documents\\GitHub\\MyWorkspace\\py_study\\img\\1.jpg"'

完整代码

(源码已上传github:python爬虫入门实战)

1、主要操作步骤写在weibo_transport模块中

2、引入crawl_weibo模块用于爬取原微博数据

3、引入upload_driver模块用于将数据发布到新账号上

4、3个模块分别位于github项目中的crawl与weibo文件夹下

weibo_transport模块

# -*- coding:utf-8 -*-

'''

Created on 6月25日

@author: wycheng

'''

from crawl import crawl_weibo

import upload_driver

from lxml import html

import requests

def getContent(card):# 获取该card下微博的内容

mblog=card['mblog']

count_img=0# 每条微博重新计数图片数,用于发送图片的地址

text= html.fromstring(mblog['text'])

text=text.xpath('string(.)')#过滤正文中的多余标签

url_img=''

if 'pics' in mblog:#有图片的存储图片

pics=mblog['pics']

for pic in pics:

url_img=pic['large']['url']

ir = requests.get(url_img)

if ir.status_code == 200:# 如果请求已成功

count_img+=1# 给图片计数

open('../img/'+str(count_img)+'.jpg', 'wb').write(ir.content)# 保存在img文件夹下,发送也从这个文件夹下找

print u'图片:'+url_img

scheme=card['scheme']

created_at=mblog['created_at']

source=mblog['source']

reposts_count=mblog['reposts_count']

comments_count=mblog['comments_count']

attitudes_count=mblog['attitudes_count']

return text,count_img,scheme,created_at,source,reposts_count,comments_count,attitudes_count# 其实返回的是一个list

#登录

uploader=upload_driver.Uploader()

uploader.login('account', 'password')# 填写你的账号密码

crawl_weibo=crawl_weibo.CrawlWeibo()

# 先爬取第34页进行发布,作为范例

# 这里的页码不是微博客户端上的页码,这个接口返回的一页只有10个card

# 博主原微博有370+条微博,所以一共有38页

cards=crawl_weibo.getWeibo('2622535523',34)# 第一个参数是用户id,第二个是页数

len_cards=len(cards)

for j in range(len_cards):

index=len_cards-1-j

card=cards[index]

# (微博的cardtype==9,实战四也有说明,转发的微博mblog里有‘retweeted_status’这个key)

if card['card_type']==9 and not 'retweeted_status' in card['mblog']:# 如果这是一条非转发的微博的card

# 获取这条微博的各个数据

content_list=getContent(card)

text,count_img,scheme,created_at,source,reposts_count,comments_count,attitudes_count=content_list

# 发送这条微博的内容到新账号

# 拼接要发送的文本

text=created_at+u' 来自 '+source+'\n'+text+' \n'+u'转发 '+str(reposts_count)+u' 评论 '+str(comments_count)+u' 点赞 '+str(attitudes_count)

path_list=[]# 图片地址list

for i in range(count_img):# 根据图片总数量生成所有图片地址

path_list.append('C:\\Users\\15850\\Documents\\GitHub\\MyWorkspace\\py_study\\img\\'+str(i+1)+'.jpg')

# 上传内容

if count_img==0:# 没有图片

uploader.upload_txt(text)

elif count_img==1:# 单张图片

uploader.upload_txt_img(text, path_list[0])

else:# 多张图片

uploader.upload_txt_multiImg(text,path_list)

uploader.send()

upload_driver模块

# -*- coding:utf-8 -*-

'''

Created on 6月25日

@author: wycheng

'''

from selenium import webdriver

from selenium.webdriver.support.ui import WebDriverWait

from time import sleep

import os

class Uploader:

driver = webdriver.Chrome(executable_path='../drivers/chromedriver.exe')# chrome浏览器驱动

# 根据验证框的存在与否判断是否要输入验证码

def isVerifyCodeExist(self):

try:# 如果成功找到验证码输入框返回true

self.driver.find_element_by_css_selector('input[name="verifycode"]')

return True

except:# 如果异常返回false

return False

# 输入验证码部分,如果无需输入则直接返回,否则手动输入成功后返回

def inputVerifyCode(self):

input_verifycode=self.driver.find_element_by_css_selector('input[name="verifycode"]')# 验证码输入框

bt_change=self.driver.find_element_by_css_selector('img[action-type="btn_change_verifycode"]')# 验证码图片,点击切换

bt_logoin=self.driver.find_element_by_class_name('login_btn')# 登录按钮

while self.isVerifyCodeExist():

print u'请输入验证码……(输入"c"切换验证码图片)'

verifycode=raw_input()

if verifycode=='c':

bt_change.click()

else:

input_verifycode.send_keys(verifycode)

bt_logoin.click()

# 点击完登录以后判断是否成功

if self.driver.current_url.split('/')[-1]=='home':

print u'登录成功'

break

else:

print u'输入的验证码不正确'

#打开微博首页进行登录的过程

def login(self,account,password):

self.driver.implicitly_wait(10)# 设置隐性等待时间,等待页面加载完成才会进行下一步,最多等待10秒

url='/'

self.driver.get(url)

#输入账号密码并登录

WebDriverWait(self.driver,10).until(lambda x:x.find_element_by_id('loginname')).send_keys(account)

self.driver.find_element_by_css_selector('input[type="password"]').send_keys(password)

bt_logoin=self.driver.find_element_by_class_name('login_btn')

bt_logoin.click()

#如果存在验证码,则进入手动输入验证码过程

if self.isVerifyCodeExist():

self.inputVerifyCode()

# 上传文字

def upload_txt(self,text):

input_w=self.driver.find_element_by_xpath('//div[@node-type="textElDiv"]/textarea[@class="W_input"]')

input_w.send_keys(text)

sleep(1)

#运行上传图片脚步

def upload_img_script(self,time_bef,time_after,path):# path参数需要前后带双引号

sleep(time_bef)# 等待弹窗时间

os.system('C:/Users/15850/Documents/GitHub/MyWorkspace/py_study/script/upload.exe '+path)

sleep(time_after)# 等待图片加载时间

# 上传文字和单图

def upload_txt_img(self,text,img_path):

self.upload_txt(text)# 将文字上传

img=self.driver.find_element_by_css_selector('a[action-type="multiimage"]')# 图片按钮

img.click()# 点击图片按钮

sleep(1)# 等待加载其他按钮

#单图/多图按钮,即上传图片按钮

bt_uploadimg=WebDriverWait(self.driver,10).until(lambda x:x.find_element_by_xpath('//object[contains(@id,"swf_upbtn")]'))

bt_uploadimg.click()# 点击上传按钮

self.upload_img_script(1,2,img_path)

# 上传文字和多图

def upload_txt_multiImg(self,text,img_path_list):

self.upload_txt_img(text,img_path_list[0])# 将文字和第一张图片上传

len_imgs=len(img_path_list)# 图片地址list的长度

bt_uploadimg=WebDriverWait(self.driver,10).until(lambda x:x.find_element_by_xpath('//li[@node-type="uploadBtn"]/div/object[contains(@id,"swf_upbtn")]'))

for i in range(len_imgs-1):# 将剩余图片上传

bt_uploadimg.click()

self.upload_img_script(1, 2,img_path_list[i+1])

# 发布

def send(self):

self.driver.find_element_by_class_name('W_btn_a').click()

sleep(4)# 等待发送成功字样消失

效果展示动图

效果展示动图

参考

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。