python爬虫爬网站数据登录_使用webdriver+urllib爬取网页数据(模拟登陆,过验证码)...

urilib是python的标准库,当我们使用Python爬取网页数据时,往往用的是urllib模块,通过调用urllib模块的urlopen(url)方法返回网页对象,并使用read()方法获得url的html内容,然后使用BeautifulSoup抓取某个标签内容,结合正则表达式过滤。但是,用urllib.urlopen(url).read()获取的只是网页的静态html内容,很多动态数据(比如网站访问人数、当前在线人数、微博的点赞数等等)是不包含在静态html里面的,例如我要抓取这个bbs网站中点击打开链接 各个板块的当前在线人数,静态html网页是不包含的(不信你查看页面源代码试试,只有简单的一行)。像这些动态数据更多的是由JavaScript、JQuery、PHP等语言动态生成的,因此再用抓取静态html内容的方式就不合适了。

本文将通过selenium webdriver模块的使用,以获取这些动态生成的内容,尤其是一些重要的动态数据。其实selenium模块的功能不是仅仅限于抓取网页,它是网络自动化测试的常用模块,在Ruby、Java里面都有广泛使用,Python里面虽然使用相对较少,但也是一个非常简洁高效容易上手的自动化测试模块。通过利用selenium的子模块webdriver的使用,解决抓取动态数据的问题,还可以对selenium有基本认识,为进一步学习自动化测试打下基础。

一 环境配置

1、下载geckodriver.exe:下载地址:https://github.com/mozilla/geckodriver/releases请根据系统版本选择下载;(如Windows 64位系统)

2、下载解压后将getckodriver.exe复制到Firefox的安装目录下,

如(C:\Program Files\Mozilla Firefox),并在环境变量Path中添加路径:C:\Program Files\Mozilla Firefox

3、安装selenium

pip install selenium

4、beautifulsoup4的安装,Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,

pip install beautifulsoup4

pip install lxml

pip install html5lib

5、安装faker

pip install faker

二 如何爬取落网某一期数据信息

落网的网址是

然后点击F12,选择第989期,进入该页面,我们就以爬取这一页的内容为例:

进入之后,我们可以看到该网页主要包括以下内容:期刊编号,期刊标题,期刊封面,期刊描述,歌单。

通过下方开发工具中的查看器,可以获取我们感兴趣数据的标签,选择器等信息。以歌单为例:

程序代码如下:

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

"""Created on Thu May 24 16:35:36 2018

@author: zy"""

importosfrom bs4 importBeautifulSoupimportrandomfrom faker importFactoryimportqueueimportthreadingimporturllib.request as urllibfrom selenium importwebdriverimporttime#"gbk" codec can't encode character "\xXX" in position XX : https://www.cnblogs.com/feng18/p/5646925.html#即字符编码是utf-8的字节,但是并不能转换成utf-8编码的字符串

'''爬取网页信息的类'''

defrandom_proxies(proxy_ips):'''从proxy_ips中随机选取一个代理ip

args:

proxy_ips:list 每个元素都是一个代理ip'''ip_index= random.randint(0, len(proxy_ips)-1)

res= { 'http': proxy_ips[ip_index] }returnresdeffix_characters(s):'''替换掉s中的一些特殊字符

args:

s:字符串'''

for c in ['', ':', '"', '/', '\\', '|', '?', '*']:

s= s.replace(c, '')returnsdefget_static_url_response_html(url):'''爬取静态页面数据:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#find

reture:

返回html代码'''fake=Factory.create()#这里配置可用的代理IP,可以写多个

proxy_ips =['183.129.151.130']

headers= {'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Charset':'GB2312,utf-8;q=0.7,*;q=0.7','Accept-Language':'zh-cn,zh;q=0.5','Cache-Control':'max-age=0','Connection':'keep-alive','Host':'John','Keep-Alive':'115','Referer':url,'User-Agent': fake.user_agent()#'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'

}

req= urllib.Request(url=url,origin_req_host=random_proxies(proxy_ips),headers =headers)#当该语句读取的返回值是bytes类型时,要将其转换成utf-8才能正常显示在python程序中

response =urllib.urlopen(req).read()#需要进行类型转换才能正常显示在python中

response = response.decode('utf-8')returnresponsedef download(url,dstpath=None):'''利用urlretrieve()这个函数可以直接从互联网上下载文件保存到本地路径下

args:

url:网页文件或者图片以及其他数据路径,尽量不要下载网页,因为下载的是静态网页

dstpath:保存全路况'''

if dstpath isNone:

dstpath= './code.jpg'

try:

urllib.urlretrieve(url,dstpath)print('Download from {} finish!'.format(url))exceptException as e:print('Download from {} fail!'.format(url))defget_dynamic_url_response_html(url):'''selenium是一个用于Web应用自动化程序测试的工具,测试直接运行在浏览器中,就像真正的用户在操作一样

selenium支持通过驱动真实浏览器(FirfoxDriver,IternetExplorerDriver,OperaDriver,ChromeDriver)

selenium支持通过驱动无界面浏览器(HtmlUnit,PhantomJs)

1、下载geckodriver.exe:下载地址:https://github.com/mozilla/geckodriver/releases请根据系统版本选择下载;(如Windows 64位系统)

2、下载解压后将getckodriver.exe复制到Firefox的安装目录下,

如(C:\Program Files\Mozilla Firefox),并在环境变量Path中添加路径:C:\Program Files\Mozilla Firefox

return:

返回html代码 获取url页面信息后关闭连接'''browser= webdriver.Firefox(executable_path = r'D:\ff\geckodriver.exe')#html请求

browser.get(url)

html=browser.page_source

time.sleep(2)#html = browser.page_source.decode('utf-8')

#关闭浏览器

browser.quit()returnhtmlclassUrlSpyder(threading.Thread):'''使用Threading模块创建线程:http://www.runoob.com/python/python-multithreading.html

使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法:

主要思路分成两部分:

1.用来发起http请求分析出播放列表然后丢到队列中

2.在队列中逐条下载文件到本地(如果需要下载文件)

一般分析列表速度较快,下载速度比较慢,可以借助多线程同时进行下载'''

def __init__(self, base_url, releate_urls, que=None):'''args:

base_url:网址

releate_urls:相对于网址的网页路径 list集合

que:队列 Python的queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,

和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。'''threading.Thread.__init__(self)print('Start spider\n')print ('=' * 50)#保存字段信息

self.base_url =base_urlif queue isNone:

self.queue=queue.Queue()else:

self.queue=que

self.releate_urls=releate_urlsdefrun(self):'''把要执行的代码写到run函数里面 线程在创建后会直接运行run函数'''

#遍历每一个网页 并开始爬取

for releate_url inself.releate_urls:

self.spider(releate_url)print ('\nCrawl end\n\n')defspider(self, releate_url):'''爬取指定网页数据,并提取我们所需要的网页信息的函数

args:

releate_url:网页的相对路径'''url=os.path.join(self.base_url,releate_url)print ('Crawling:' + url + '\n')'''解析html 针对不同的网址,解析内容也不一样'''

#获取指定网页的html代码

response =get_dynamic_url_response_html(url)#response = getUrlRespHtml(url) )

#使用BeautifulSoup解析这段代码,能够得到一个 BeautifulSoup 的对象,并能按照标准的缩进格式的结构输出:

#soup = BeautifulSoup(response, 'lxml')

soup = BeautifulSoup(response, 'html.parser')#输出网页信息

#print(soup.prettify())

#with open('./read.html','w',encoding='utf-8') as f:

#两个内容是一样的 只是一个是标准缩进格式,另一个不是

#f.write(str(soup.prettify()))

#f.write(str(response))

'''根据爬取网站不同,下面代码也会不一样

解析该网站某一期的所有音乐信息

主要包括:

期刊信息

期刊封面

期刊描述

节目清单'''

#989

num = soup.find('span',class_='vol-number').get_text()#曙色初动 解析标题 find每次只返回第一个元素

title = soup.find('span',class_='vol-title').get_text()'''

曙色初动

  

解析对应的图片背景'''cover= soup.find('img', class_='vol-cover').get('src')'''

本期音乐为史诗氛围类音乐专题。
史诗音乐的美好之处在于能够让人有无限多的宏伟想象。就像这一首首曲子,用岁月沧桑的厚重之声,铿锵有力的撞击人的内心深处,绽放出人世间的悲欢离合与决绝!
Cover From Meer Sadi

解析描述文本'''desc= soup.find('div', class_='vol-desc').get_text()'''01. Victory

02. Mythical Hero

解析歌单 find_all返回一个列表 所有元素'''track_names= soup.find_all('a', attrs={'class': 'trackname'})

track_count=len(track_names)

tracks=[]#12期前的音乐编号1~9是1位(如:1~9),之后的都是2位 1~9会在左边垫0(如:01~09)

for track intrack_names:

_id= str(int(track.text[:2])) if (int(releate_url) < 12) else track.text[:2]

_name= fix_characters(track.text[4:])

tracks.append({'id': _id, 'name': _name})

phases={'phase': num, #期刊编号

'title': title, #期刊标题

'cover': cover, #期刊封面

'desc': desc, #期刊描述

'track_count': track_count, #节目数

'tracks': tracks #节目清单(节目编号,节目名称)

}#追加到队列

self.queue.put(phases)if __name__ == '__main__':

luoo_site= 'http://www.luoo.net/vol/index/'spyder_queue=queue.Queue()## 创建新线程

luoo = UrlSpyder(luoo_site, releate_urls=['1372','1370'], que=spyder_queue)

luoo.setDaemon(True)#开启线程

luoo.start()'''从队列中取数据,并进行下载'''count= 1

whileTrue:if spyder_queue.qsize() <=0:

time.sleep(10)pass

else:

phases=spyder_queue.get()print(phases)#下载图片

download(phases['cover'],'%d.jpg'%(count))

count+= 1

运行结果如下:

并爬取了如下两种图片:

注意:我们一般使用urllib.urlretrieve()函数下载图片,文件等,但是不要使用这个函数下载网页,因为下载下来也是静态网页。如果要保存网页html内容,我们使用selenium获取网页内容,然后写入到指定文件:

with open(file_name, 'w',encoding='utf-8') as f:

f.write(self.browser.page_source)

三 使用selenium模拟登陆

刚才我们爬取的网站是不需要登陆的,如果遇到一个需要先登录,才能爬取数据怎么办?幸运的是selenium提供了模拟鼠标点击,和键盘输入的功能,下面我们将会演示一个登陆微博的程序,并利用微博进行搜索我们需要的内容,并把数据保存下来。

下面我先列入完整代码,一会一点点讲解:

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

"""Created on Thu May 24 16:35:36 2018

@author: zy"""

importosimporttimefrom selenium importwebdriver#from selenium.common.exceptions import NoSuchElementException

from bs4 importBeautifulSoupclassWeiboSpyder():'''python爬虫——基于selenium用火狐模拟登陆爬搜索关键词的微博:https://blog.csdn.net/u010454729/article/details/51225388

0.安装火狐

1.安装selenium,可通过pip安装:pip install selenium

2.程序里面改三处:用户名、密码、搜过的关键词search_text

3.需要手动输入验证码,并且验证码大小写敏感,若是输错了,等3秒再输入。

4.爬下来了,用BeautifulSoup提取,并且将相同文本的放在一起,并且排序

时间:5秒一个,不可缩短,不然加载不出来下一页这个按钮,然后程序就挂了,若网速快些,延时可以短些。 在每个点击事件之前或者之后都加time.sleep(2)'''

def __init__(self):#微博登录用户名,用户命名

self.username_ = "你的用户名"self.password_= "你的密码"self.href= 'http://s.weibo.com/'

#获取火狐浏览器对象

self.browser =webdriver.Firefox(executable_path = r'D:\ff\geckodriver.exe')#html请求

self.browser.get(self.href)

time.sleep(2)#获取网页右上登陆元素 并点击

login_btn = self.browser.find_element_by_xpath('//a[@node-type="loginBtn"]')

login_btn.click()

time.sleep(2)#获取选择账号登录元素 并点击

name_login = self.browser.find_element_by_xpath('//a[@action-data="tabname=login"]')

name_login.click()

time.sleep(2)#获取输入用户名,密码元素 并输入用户名和密码

username = self.browser.find_element_by_xpath('//input[@node-type="username"]')

password= self.browser.find_element_by_xpath('//input[@node-type="password"]')

username.clear()

username.send_keys(self.username_)

password.clear()

password.send_keys(self.password_)#获取提交登陆元素 并点击

sub_btn = self.browser.find_element_by_xpath('//a[@suda-data="key=tblog_weibologin3&value=click_sign"]')

sub_btn.click()

time.sleep(5)'''#下面是验证码部分,如果需要验证码的化

while True:

try:

verify_img = browser.find_element_by_xpath('//img[@node-type="verifycode_image"]')

except NoSuchElementException:

break

if verify_img:

# 输入验证码

verify_code = browser.find_element_by_xpath('//input[@node-type="verifycode"]')

verify_code_ = input('verify_code > ')

verify_code.clear()

verify_code.send_keys(verify_code_)

# 提交登陆

sub_btn = browser.find_element_by_xpath('//a[@suda-data="key=tblog_weibologin3&value=click_sign"]')

sub_btn.click()

time.sleep(2)

else:

break'''

#获取搜索栏元素

#self.search_form = self.browser.find_element_by_xpath('//input[@class="searchInp_form"]')

def get_weibo_search(self,search_text,max_length = 20):'''在网页中搜索指定信息,搜多到一页信息后,保存,然后搜索下一页信息,直至到max_length页

默认在当前路径下生成一个 名字为 search_text(去除下划线) 的文件夹,下面存放爬取得网页

args:

search_text:需要搜索的内容

max_length:最大爬取网页个数

return:

dst_dir:返回爬取网页所在的文件夹'''

#将关键词送到搜索栏中,进行搜索

self.search_form = self.browser.find_element_by_xpath('//input[@class="searchInp_form"]')

self.search_form.clear()

self.search_form.send_keys(search_text)

time.sleep(5)#获取搜索按钮元素 只有窗口最大化 才有搜索按钮

self.search_btn = self.browser.find_element_by_xpath('//a[@class="searchBtn"]')#点击搜索

self.search_btn.click()#进入循环之前,让第一页先加载完全。

time.sleep(2)print('Try download html for : {}'.format(search_text))

topics_name=search_text#将名字里面的空格换位_ 创建以搜索内容为名字的文件夹,保存爬取下来的网页

topics_name = topics_name.replace(" ","_")

os_path=os.getcwd()

dst_dir=os.path.join(os_path,topics_name)if notos.path.isdir(dst_dir):

os.mkdir(dst_dir)#捕获异常,有的搜索可能没有下一页 遇到错误会跳过

try:

count= 1

while count <=max_length:'''#保存网页 构建目标文件路径'''file_name= os.path.join(dst_dir,'{}_{}.html'.format(topics_name, count))#必须指定编码格式

with open(file_name, 'w',encoding='utf-8') as f:

f.write(self.browser.page_source)print('Page {} download finish!'.format(count))

time.sleep(3)#获取下一页元素

self.next_page = self.browser.find_element_by_css_selector('a.next')#next_page = browser.find_element_by_xpath('//a[@class="page next S_txt1 S_line1"]')

#有的时候需要手动按F5刷新,不然等太久依然还是出不来,程序就会挂,略脆弱。

self.next_page.click()

count+= 1

#完成一轮之前,保存之前,先让其加载完,再保存 如果报错,可以通过调节时间长度去除错误

time.sleep(10)exceptException as e:print('Error:',e)returndst_dirdefget_weibo_text(self,file_name):'''将html文件里面的

args:

text:返回一个list 每一个元素都是p标签的内容

args:

file_name:html文件路径'''text=[]

soup= BeautifulSoup(open(file_name,encoding='utf-8'),'lxml')#获取tag为div class_="WB_cardwrap S_bg2 clearfix"的所有标签

items = soup.find_all("div",class_="WB_cardwrap S_bg2 clearfix")if notitems:

text=[]#遍历每一额标签,提取为p的子标签内容

for item initems:

line= item.find("p").get_text()#print line

text.append(line)returntextdefget_weibo_all_page(self,path):'''文件夹下所有文件内容提取出来,然后合并起来

args:

path:list 每个元素都是一个文件路径,加入我们在新浪搜索内容为火影忍者,则在当前路径下会生成一个为火影忍者的文件夹

path就是这个文件夹的路径

return:

texts_all:返回合并之后的内容 是一个list'''texts_all=[]

file_names=os.listdir(path)#遍历当前文件夹下每个文件

for file_name infile_names:#将html文件里面的

texts =self.get_weibo_text(os.path.join(path,file_name))#遍历每一个元素

for text intexts:

text= text.replace("\t","")

text= text.strip("\n")

text= text.strip(" ")#若是重了,不加入到里面

if text intexts_all:pass

else:

texts_all.append(text)returntexts_alldefget_results_weibo(self,weibos_name):'''合并若干个文件夹下提取出来的微博

args:

weibos_name:list 每一个元素都是一个搜索项提取出来的文本文件

args:'''texts=[]for file_name inweibos_name:

with open(file_name,encoding='utf-8') as f:

text=f.readlines()for line intext:

line= line.strip("\n")if line not intexts:

texts.append(line)returntextsif __name__ == '__main__':print('开始搜索')

search=WeiboSpyder()#在新浪搜索中需要搜索内容

searchs_text = ["火影忍者","苍井空","波多野结衣"]#遍历要搜索的每一行

for search_text insearchs_text:

path= search.get_weibo_search(search_text,max_length=5)

texts_all=search.get_weibo_all_page(path)#文本排序

texts_all_sorted =sorted(texts_all)

weibo_text_name= path + "_weibos.txt"with open(weibo_text_name,"w",encoding='utf-8') as f:#一行一行的写入

for text intexts_all_sorted:

f.write(text+ "\n")#将几个_weibos.txt文件合并到一起

print("Together:")

file_names_weibos= [i for i in os.listdir(os.getcwd()) if i.endswith("_weibos.txt")]

texts=search.get_results_weibo(file_names_weibos)

with open("results.txt","w",encoding='utf-8') as f:for text insorted(texts):

f.write(text+"\n")print("Together finish!")

微博的官网是http://s.weibo.com/,同样我们点击F12,打开网页:我们先获取右上角登陆按钮的标签信息:

我们怎样可以实现点击这个登陆的事件呢,在上面代码可以看到我定义了一个WeiboSpyder的类,其中在构造函数中负责网页的登陆功能。

我们首先通过向指定网址发送html请求,进行加载网页:

#获取火狐浏览器对象

self.browser =webdriver.Firefox(executable_path = r'D:\ff\geckodriver.exe')#html请求

self.browser.get(self.href)

time.sleep(2)

然后通过find_element_by_xpath()函数获取登陆标签,获取该标签之后,我们触发点击事件,为了等到网页加载出来,我们在点击时候,睡眠2s。

#获取选择账号登录元素 并点击

name_login = self.browser.find_element_by_xpath('//a[@action-data="tabname=login"]')

name_login.click()

time.sleep(2)

同理,后面我们获取选择账号登陆标签,然后获取登陆名,登录密码元素,然后利用send_keys()方法输入登陆信息,最后获取登陆标签,开始登陆:

#获取选择账号登录元素 并点击

name_login = self.browser.find_element_by_xpath('//a[@action-data="tabname=login"]')

name_login.click()

time.sleep(2)#获取输入用户名,密码元素 并输入用户名和密码

username = self.browser.find_element_by_xpath('//input[@node-type="username"]')

password= self.browser.find_element_by_xpath('//input[@node-type="password"]')

username.clear()

username.send_keys(self.username_)

password.clear()

password.send_keys(self.password_)#获取提交登陆元素 并点击

sub_btn = self.browser.find_element_by_xpath('//a[@suda-data="key=tblog_weibologin3&value=click_sign"]')

sub_btn.click()

time.sleep(5)

由于这里不需要验证码登陆,我们就跳过验证码了,在下一个案例中我会教大家如何过验证码。

完成登陆后,我们在主程序中开始利用微博的搜索引擎搜索指定内容:

if __name__ == '__main__':print('开始搜索')

search=WeiboSpyder()#在新浪搜索中需要搜索内容

searchs_text = ["火影忍者","苍井空","波多野结衣"]#遍历要搜索的每一行

for search_text insearchs_text:

path= search.get_weibo_search(search_text,max_length=5)

texts_all=search.get_weibo_all_page(path)#文本排序

texts_all_sorted =sorted(texts_all)

weibo_text_name= path + "_weibos.txt"with open(weibo_text_name,"w",encoding='utf-8') as f:#一行一行的写入

for text intexts_all_sorted:

f.write(text+ "\n")#将几个_weibos.txt文件合并到一起

print("Together:")

file_names_weibos= [i for i in os.listdir(os.getcwd()) if i.endswith("_weibos.txt")]

texts=search.get_results_weibo(file_names_weibos)

with open("results.txt","w",encoding='utf-8') as f:for text insorted(texts):

f.write(text+"\n")print("Together finish!")

我在这里搜索的内容主要包括:

searchs_text = ["火影忍者","苍井空","波多野结衣"]

然后遍历每一项,开始调用get_weibo_search()方法:

def get_weibo_search(self,search_text,max_length = 20):'''在网页中搜索指定信息,搜多到一页信息后,保存,然后搜索下一页信息,直至到max_length页

默认在当前路径下生成一个 名字为 search_text(去除下划线) 的文件夹,下面存放爬取得网页

args:

search_text:需要搜索的内容

max_length:最大爬取网页个数

return:

dst_dir:返回爬取网页所在的文件夹'''

#将关键词送到搜索栏中,进行搜索

self.search_form = self.browser.find_element_by_xpath('//input[@class="searchInp_form"]')

self.search_form.clear()

self.search_form.send_keys(search_text)

time.sleep(5)#获取搜索按钮元素 只有窗口最大化 才有搜索按钮

self.search_btn = self.browser.find_element_by_xpath('//a[@class="searchBtn"]')#点击搜索

self.search_btn.click()#进入循环之前,让第一页先加载完全。

time.sleep(2)print('Try download html for : {}'.format(search_text))

topics_name=search_text#将名字里面的空格换位_ 创建以搜索内容为名字的文件夹,保存爬取下来的网页

topics_name = topics_name.replace(" ","_")

os_path=os.getcwd()

dst_dir=os.path.join(os_path,topics_name)if notos.path.isdir(dst_dir):

os.mkdir(dst_dir)#捕获异常,有的搜索可能没有下一页 遇到错误会跳过

try:

count= 1

while count <=max_length:'''#保存网页 构建目标文件路径'''file_name= os.path.join(dst_dir,'{}_{}.html'.format(topics_name, count))#必须指定编码格式

with open(file_name, 'w',encoding='utf-8') as f:

f.write(self.browser.page_source)print('Page {} download finish!'.format(count))

time.sleep(3)#获取下一页元素

self.next_page = self.browser.find_element_by_css_selector('a.next')#next_page = browser.find_element_by_xpath('//a[@class="page next S_txt1 S_line1"]')

#有的时候需要手动按F5刷新,不然等太久依然还是出不来,程序就会挂,略脆弱。

self.next_page.click()

count+= 1

#完成一轮之前,保存之前,先让其加载完,再保存 如果报错,可以通过调节时间长度去除错误

time.sleep(10)exceptException as e:print('Error:',e)return dst_dir

这个函数主要就做了以下事情:

将关键词送到搜索栏中,进行搜索

创建一个文件夹,把刚才搜索的网页保存下来

获取下一页元素,并触发点击事件,然后再把新的网页保存下来

重复上一步,直至保存了max_length页,或者触发的异常,停止搜索

返回文件夹路径

程序运行结果如下:

如果我们运行出现类似以下的错误:

这主要是因为我们页面没有加载完,所以才会找不到指定元素,我们可以通过延长睡眠时间解决该问题。

并且我们会生成三个文件夹:

为什么搜索苍井空和波多野结衣只有一条数据,主要是因为被屏蔽了:

后面的代码get_weibo_all_page()函数以及get_weibo_text(),get_results_weibo()函数就是利用BeautifulSoup对刚才获取的几个网页内容进行提取,这里就不过多介绍了,最终还会生成几个文件:

results.txt是对另外三个文件进行合并,内容如下:

四 爬取需要验证码的网站

其实爬取需要验证码网站和上一个案例是类似的,只不过需要我们输入验证码,如何过验证码主要有两种方式,一种是利用手动输入,还有一种是采用自动识别技术识别出验证码,验证码的识别需要对图像处理具有一定的基础,或者我们利用人工神经网络识别,但是这里为了简单,我们可以采用

tesseract.exe这个软件来识别,不过识别准确率很低,这个软件使用之前需要准备工作

配置环境变量 TESSDATE PREFIX = D:\Tesseract-OCR

然后我们可以安装相对应的python库,来调用该软件:

pip install pytesser3

不过我使用的时候总是报错,因此就把该库下载了,并修改了原来库的代码,保存在pytesser.py

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

"""Created on Sat May 26 09:19:09 2018

@author: lenovo"""

importsubprocessimportosfrom PIL importImage'''使用之前需要准备工作

1.下载tesseract.exe https://www.polarxiong.com/archives/python-pytesser-tesseract.html

安装在D:\Tesseract-OCR

2.配置环境变量 TESSDATE PREFIX = D:\Tesseract-OCR'''pwd=os.getcwd()#tesseract.exe在命令行调用的命令 或者tesseract.exe文件的全路径

tesseract_exe_name = 'D:\\Tesseract-OCR\\tesseract'

#把图像保存成tesseract兼容格式bmp

scratch_image_name = pwd + os.sep + "temp.bmp"

#识别后txt文件保存的路径

scratch_text_name_root = pwd + os.sep + "temp"

#识别之后是否清楚生成的临时文件

cleanup_scratch_flag =Truedefimage_to_scratch(im, scratch_image_name):'''Saves image in memory to scratch file. .bmp format will be read correctly by Tesseract

保存图像到路径scratch_image_name 即把图片转换成Tesseract兼容格式 bmp'''im.save(scratch_image_name, dpi=(200,200))defretrieve_text(scratch_text_name_root):'''从识别的txt文件中读取文本'''with open(scratch_text_name_root+ '.txt','r',encoding='utf-8') as f:

text=f.read()returntextdefperform_cleanup(scratch_image_name, scratch_text_name_root):'''清除临时文件'''

for name in (scratch_image_name, scratch_text_name_root + '.txt', "tesseract.log"):try:

os.remove(name)exceptOSError:pass

defcall_tesseract(input_filename, output_filename):'''Calls external tesseract.exe on input file (restrictions on types),

outputting output_filename +"txt"

调用 tesseract.exe识别图片'''

print(tesseract_exe_name,input_filename,output_filename)

args=[tesseract_exe_name, input_filename, output_filename]

proc=subprocess.Popen(args)

retcode=proc.wait()if retcode!=0:print('Open process error')def image_to_string(im, cleanup =cleanup_scratch_flag):"""Converts im to file, applies tesseract, and fetches resulting text.

If cleanup=True, delete scratch files after operation."""

try:#保存图片为scratch_image_name

image_to_scratch(im, scratch_image_name)#调用tesseract生成识别文本

call_tesseract(scratch_image_name, scratch_text_name_root)#提取文本内容

text =retrieve_text(scratch_text_name_root)finally:ifcleanup:

perform_cleanup(scratch_image_name, scratch_text_name_root)returntextdef image_file_to_string(filename, cleanup = cleanup_scratch_flag, graceful_errors=True):"""Applies tesseract to filename; or, if image is incompatible and graceful_errors=True,

converts to compatible format and then applies tesseract. Fetches resulting text.

If cleanup=True, delete scratch files after operation."""

try:try:

call_tesseract(filename, scratch_text_name_root)

text=retrieve_text(scratch_text_name_root)except:

im=Image.open(filename)

text=image_to_string(im, cleanup)finally:ifcleanup:

perform_cleanup(scratch_image_name, scratch_text_name_root)return text

下面我以爬取超星网站的评论数据为例:网址http://passport2.chaoxing.com/login?fid=2218&refer=http://i.mooc.chaoxing.com

按下F12,打开该网址,我们可以看到有一处需要输入验证码:

同样我定义了一个chaoxingSpy类,其中有两个函数是用来处理验证码的:

def read_code(self,file=None,auto=True):'''识别验证码

args:

file:验证码图片所在本机路径,如果为None,则自动从网页中截取验证码图片保存到'./code.jpg'

auto:表示是否自动识别 为True:自动识别 False:手动识别 自动识别准确的不高'''

'''对验证码进行区域截图,并保存'''

if file isNone:#裁切后的验证码路径

file = './code.jpg'

#对网页进行全截图

self.browser.get_screenshot_as_file('./all.jpg')

img= Image.open('./all.jpg')#设置要裁剪的区域

box = (490,340,570,370)#裁切

region =img.crop(box)#RGBA to RGB

region= region.convert('RGB')

region.save(file)print('图片裁切成功!')else:

img=Image.open(file)ifauto:

verify_code=self.image_file_to_string(file)else:#显示图片

region.show()#手动输入验证码 获取验证码输入元素

verify_code = input('verify_code >')returnverify_codedefimage_file_to_string(self,file):'''图像增强,自动识别简单验证码

args:

file:验证码文件的路径

return:

code:识别的验证码'''img=Image.open(file)#图像加强,二值化

imgry = img.convert('L')#对比度增强

sharpness =ImageEnhance.Contrast(imgry)

sharp_img= sharpness.enhance(2.0)#保存

sharp_img.save(file)

code=pytesser.image_file_to_string(file)#去除非数字

new_code = ''

for i incode:if i in '0123456789':

new_code+=iprint('识别的验证码为:',new_code)return new_code

当read_code()函数的auto参数传入False时,表示手动输入验证码,即在程序运行的时候,我们利用input()函数读取我们控制台输入的验证码,然后返回验证码的值。当传入True的时候,我们通过调用tesseract.exe识别验证码,为了提高准确率,我们事先对图像进行了处理,默认下我们截取当前网页的验证码图片,然后通过对图片二值化,对比度增强处理然后在进行自动识别。

完整代码如下:

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

"""Created on Fri May 25 18:04:13 2018

@author: zy"""

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

"""Created on Thu May 24 16:35:36 2018

@author: zy"""

importtimefrom selenium importwebdriverfrom selenium.common.exceptions importNoSuchElementExceptionimporturllib.request as urllibfrom PIL importImage,ImageEnhanceimportpytesser'''webdriver使用说明:https://wenku.baidu.com/view/d64005c6af45b307e9719715.html'''

classchaoxingSpy():def __init__(self):'''登录系统'''

#登录用户名,用户命名

self.username_ = "你的用户名"self.password_= "你的密码"url= 'http://passport2.chaoxing.com/login?fid=2218&refer=http://i.mooc.chaoxing.com'

#获取火狐浏览器对象

self.browser = webdriver.Firefox(executable_path = r'D:\ff\geckodriver.exe')#html请求

self.browser.get(url)#存放登录前后的cookies

self.cookies = {'before':'','after':''}

self.cookies['before'] =self.browser.get_cookies()print('登陆前:\n')for cookie in self.cookies['before']:print('%s -> %s' % (cookie['name'], cookie['value']))#登录系统,循环尝试登录,登录成功,就会抛异常,跳出循环继续执行

whileTrue:try:#获取登录用户名元素

username = self.browser.find_element_by_id('unameId')#获取登录密码元素

password = self.browser.find_element_by_id('passwordId')#输入用户名和密码

username.clear()

username.send_keys(self.username_)

password.clear()

password.send_keys(self.password_)#判断是否需要输入验证码

try:#获取验证码图片

verify_img = self.browser.find_element_by_id('numVerCode')#获取验证码url 就是验证码产生的网页

#code_src= verify_img.get_attribute('src')

exceptNoSuchElementException:print('不需要输入验证码进行验证!')ifverify_img:#获取验证码

verify_code_ = self.read_code(auto=False)

verify_code= self.browser.find_element_by_id('numcode')

verify_code.clear()

verify_code.send_keys(verify_code_)#提交登录

login_btn = self.browser.find_element_by_xpath('//input[@class="zl_btn_right"]')

login_btn.click()

time.sleep(3)#判断是否已经登录进系统

if url ==self.browser.current_url:#刷新网页

self.browser.refresh()except:#cookies = self.browser.get_cookies()

print('登录成功!')#存放登录前后的cookies

self.cookies['after'] =self.browser.get_cookies()print('登陆后:\n')for cookie in self.cookies['after']:print('%s -> %s' % (cookie['name'], cookie['value']))break

#利用登陆后的cookies 跳转到需要爬取的页面

url = 'http://mooc1.chaoxing.com/mycourse/teachercourse?moocId=93196686&clazzid=3216311&edit=true'self.browser.get(url)

time.sleep(2)#点击超链接打开一个新的窗口

moretipic_a = self.browser.find_element_by_xpath('//a[@class="moreTopic"]')

moretipic_a.click()

time.sleep(2)#0是第一个打开的窗口 1是最后一个打开的窗口 转到最后一个窗口 超链接的点击最好都要加这句代码

#https://www.cnblogs.com/nullbaby/articles/7205247.html

#https://www.cnblogs.com/studyddup0212/p/9030455.html

self.browser.switch_to_window(self.browser.window_handles[1])

time.sleep(2)defget_text(self):'''获取所有的文本'''texts=[]

count= 1

#写文件

with open('./out_topics.txt', 'w',encoding='utf-8') as f:'''爬取文件夹外部数据'''txts=self.get_url_txt()for txt intxts:

txt= '%s. %s'%(count,txt)

texts.append(txt)

f.write(txt)

count+= 1

print('外部数据写入成功!')'''遍历每一个文件夹的数据'''with open('./in_topics.txt', 'w',encoding='utf-8') as f:

folder_urls=self.spyder_folder()for url infolder_urls:

txts=self.get_url_txt(url)for txt intxts:

txt= '%s. %s'%(count,txt)

texts.append(txt)

f.write(txt)

count+= 1

print('文件夹数据写入成功!')def get_url_txt(self,url=None):'''遍历一个网页中的所有数据

args:

url:网页网址 如果是None,就是从当前网页读取数据

return:

texts:获取所有数据 list类型 每个元素对应一行数据'''texts=[]ifurl:#html请求

self.browser.get(url)

time.sleep(2)

count= 1

whileTrue:try:#如果有查看更多,获取查看更多的超链接 循环次数越多,等待时间越长

getmoretopic_a = self.browser.find_element_by_id('getMoreTopic')

getmoretopic_a.click()

time.sleep(5)print(count,'点击查看更多成功!')

count+= 1

if count> 40:

self.browser.switch_to_window(self.browser.window_handles[1])

time.sleep(20)break

except:

self.browser.switch_to_window(self.browser.window_handles[1])

time.sleep(5)break

#获取所有话题

divlist = self.browser.find_elements_by_class_name('content1118')#遍历每一个话题

for div indivlist:

name= div.find_element_by_class_name('name').text

date= div.find_element_by_class_name('gray').text

text= div.find_element_by_class_name('stuFont').find_element_by_tag_name('span').text

row= '%s %s : %s\n'%(date,name,text)

texts.append(row)#print(row)

print('Download from {0} finish!'.format(self.browser.current_url))returntextsdefspyder_folder(self):'''爬取所有的文件夹路径

args:

folderList:list每一个元素对应一个文件夹的网址'''ul= self.browser.find_element_by_xpath('//ul[@class="folderList"]')

folderList=[]

alist= ul.find_elements_by_tag_name('a')for a inalist:

folderList.append(a.get_attribute('href'))returnfolderListdef read_code(self,file=None,auto=True):'''识别验证码

args:

file:验证码图片所在本机路径,如果为None,则自动从网页中截取验证码图片保存到'./code.jpg'

auto:表示是否自动识别 为True:自动识别 False:手动识别 自动识别准确的不高'''

'''对验证码进行区域截图,并保存'''

if file isNone:#裁切后的验证码路径

file = './code.jpg'

#对网页进行全截图

self.browser.get_screenshot_as_file('./all.jpg')

img= Image.open('./all.jpg')#设置要裁剪的区域

box = (490,340,570,370)#裁切

region =img.crop(box)#RGBA to RGB

region= region.convert('RGB')

region.save(file)print('图片裁切成功!')else:

img=Image.open(file)ifauto:

verify_code=self.image_file_to_string(file)else:#显示图片

region.show()#手动输入验证码 获取验证码输入元素

verify_code = input('verify_code >')returnverify_codedefimage_file_to_string(self,file):'''图像增强,自动识别简单验证码

args:

file:验证码文件的路径

return:

code:识别的验证码'''img=Image.open(file)#图像加强,二值化

imgry = img.convert('L')#对比度增强

sharpness =ImageEnhance.Contrast(imgry)

sharp_img= sharpness.enhance(2.0)#保存

sharp_img.save(file)

code=pytesser.image_file_to_string(file)#去除非数字

new_code = ''

for i incode:if i in '0123456789':

new_code+=iprint('识别的验证码为:',new_code)returnnew_codedef download(self,url,dstpath=None):'''利用urlretrieve()这个函数可以直接从互联网上下载文件保存到本地路径下

args:

url:网页文件或者图片以及其他数据路径

dstpath:保存全路况'''

if dstpath isNone:

dstpath= './code.jpg'

try:

urllib.urlretrieve(url,dstpath)print('Download from {} finish!'.format(url))exceptException as e:print('Download from {} fail!'.format(url))if __name__ == '__main__':#加载页面

spy =chaoxingSpy()

spy.get_text()

我们主要是爬取下面网页中的评论人时间,评论人姓名,评论人内容:

注意:在程序运行时如点击下面的查看更多话题,会创建一个新的窗口:

而我们需要获取的数据保存在讨论列表页面的话,这时候我们需要切换窗口,代码如下:

#点击超链接打开一个新的窗口

moretipic_a = self.browser.find_element_by_xpath('//a[@class="moreTopic"]')

moretipic_a.click()

time.sleep(2)#0是第一个打开的窗口 1是最后一个打开的窗口 转到最后一个窗口 超链接的点击最好都要加这句代码

#https://www.cnblogs.com/nullbaby/articles/7205247.html

#https://www.cnblogs.com/studyddup0212/p/9030455.html

self.browser.switch_to_window(self.browser.window_handles[1])

time.sleep(2)

程序运行后,会生成另个文件out_topics.txt和in_topics.txt,下面我们显示其中一个文件的的内容:

为了避免我们每次都要进行登录,我们可以利用cookies绕过登录,详细内容可以搜索其他博客。

参考文献

你可能感兴趣的:(python爬虫爬网站数据登录)