设为首页 收藏本站
查看: 862|回复: 0

[经验分享] python网络爬虫之---体验篇BeautifulSoup

[复制链接]

尚未签到

发表于 2017-4-30 10:40:57 | 显示全部楼层 |阅读模式
Python抓取网页方法,任务是批量下载网站上的文件。对于一个刚刚入门python的人来说,在很多细节上都有需要注意的地方,以下就分享一下我在初学python过程中遇到的问题及解决方法。
 
1、用Python抓取网页
 
[python] view plaincopyprint?


  • import urllib2,urllib      
  • url = 'http://www.baidu.com'    
  • req = urllib2.Request(url)    
  • content = urllib2.urlopen(req).read()  

 
1)、url为网址,需要加'http://'
2)、content为网页的html源码
问题:
1、网站禁止爬虫,不能抓取或者抓取一定数量后封ip
解决:伪装成浏览器进行抓取,加入headers:
[python] view plaincopyprint?


  • import urllib2,urllib    
  • headers = { #伪装为浏览器抓取    
  •         'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'    
  •     }    
  • req = urllib2.Request(url,headers=headers)    
  • content = urllib2.urlopen(req).read()  

  
更复杂的情况(需要登录,多线程抓取)可参考:http://www.pythonclub.org/python-network-application/observer-spider
 
2、抓取网页中的中文为乱码问题
解决:用BeautifulSoup解析网页(BeautifulSoup是Python的一个用于解析网页的插件,其安装及使用方法下文会单独讨论)
首先需要介绍一下网页中的中文编码方式,一般网页的编码会在标签中标出,目前有三种,分别是GB2312,GBK,GB18030,三种编码是兼容的,
从包含的中文字符个数比较:GB2312 < GBK < GB18030,因此如果网页标称的编码为GB2312,但是实际上用到了GBK或者GB18030的中文字符,那么编码工具就会解析错误,导致编码退回到最基本的windows-2152了。所以解决此类问题分两种情况。
1)、若网页的实际的中文编码和其标出的相符的话,即没有字符超出所标称的编码,下面即可解决
[python] view plaincopyprint?


  • import urllib,urllib2,bs4    
  • req = urllib2.Request(url)    
  • content = urllib2.urlopen(req).read()    
  • content = bs4.BeautifulSoup(content)    
  • return content  

  
2)、若网页中的中文字符超出所标称的编码时,需要在BeautifulSoup中传递参数from_encoding,设置为最大的编码字符集GB18030即可
 
[python] view plaincopyprint?


  • import urllib,urllib2,bs4    
  •         
  • req = urllib2.Request(url)    
  • content = urllib2.urlopen(req).read()    
  • content = bs4.BeautifulSoup(content,from_encoding='GB18030')    
  • return content  

  
详细的中文乱码问题分析参见:http://againinput4.blog.163.com/blog/static/1727994912011111011432810/


 
2、用Python下载文件
使用Python下载文件的方法有很多,在此只介绍最简单的一种
[python] view plaincopyprint?


  • import urllib    
  • urllib.urlretrieve(url, filepath)  

  
url为下载链接,filepath即为存放的文件路径 文件名
更多Python下载文件方法参见:http://outofmemory.cn/code-snippet/83/sanzhong-Python-xiazai-url-save-file-code
 
3、使用正则表达式分析网页
将网页源码抓取下来后,就需要分析网页,过滤出要用到的字段信息,通常的方法是用正则表达式分析网页,一个例子如下:
[python] view plaincopyprint?


  • import re    
  • content = ''    
  • match = re.compile(r'(?<=href=["]).*?(?=["])')    
  • rawlv2 = re.findall(match,content)  

  
用re.compile()编写匹配模板,用findall查找,查找content中所有与模式match相匹配的结果,返回一个列表,上式的正则表达式意思为匹配以‘href="'起始,以'"'结束的字段,使用非贪婪的规则,只取中间的部分
 
关于正则表达式,系统的学习请参见:http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html
或 http://wiki.ubuntu.org.cn/Python正则表达式操作指南
个人推荐第一篇,条理清晰,不重不漏
在此就不赘述正则表达式的学习,只总结一下我在实际写正则时的认为需要注意的几个问题:
1)、一定要使用非贪婪模式进行匹配,即*?, ?(后加?),因为Python默认使用贪婪模式进行匹配,例如'a.*b',它会匹配文档中从第一个a和最后一个b之间的文本,也就是说如果遇到一个b,它不会停止,会一直搜索至文档末尾,直到它确认找到的b是最后一个。而一般我们只想取某个字段的值,贪婪模式既不能返回正确的结果,还大大浪费了时间,所以非贪婪是必不可少的
2)、raw字符串的使用:如果要匹配一个.,*这种元字符,就需要加'\'进行转义,即要表示一个'\',正则表达式需要多加一个转义,写成'\\',但是Python字符串又需要对其转义,最终变成re.compile('\\\\'),这样就不易理解且很乱,使用raw字符串让正则表达式变得易读,即写成re.compile(r'\\'),另一个方法就是将字符放到字符集中,即[\],效果相同
3)、()特殊构造的使用:一般来说,()中的匹配模式作为分组并可以通过标号访问,但是有一些特殊构造为例外,它们适用的情况是:我想要匹配href="xxxx"这个模式,但是我只需要xxxx的内容,而不需要前后匹配的模式,这时就可以用特殊构造(?<=),和(?=)来匹配前后文,匹配后不返回()中的内容,刚才的例子便用到了这两个构造。
4)、逻辑符的使用:如果想匹配多个模式,使用'|'来实现,比如
  re.compile(r'.htm|.mid$')
 
匹配的就是以.htm或.mid结尾的模式,注意没有'&'逻辑运算符
 
3、使用BeautifulSoup分析网页
BeautifulSoup是Python的一个插件,用于解析HTML和XML,是替代正则表达式的利器,下文讲解BS4的安装过程和使用方法
1、安装BS4
下载地址:http://www.crummy.com/software/BeautifulSoup/#Download
下载 beautifulsoup4-4.1.3.tar.gz,解压:linux下 tar xvf beautifulsoup4-4.1.3.tar.gz,win7下直接解压即可
linux:
进入目录执行:
 
 1, python setup.py build 
 2, python setup.py install 
或者easy_install BeautifulSoup
win7:
cmd到控制台 -> 到安装目录 -> 执行上面两个语句即可
 
2、使用BeautifulSoup解析网页
本文只介绍一些常用功能,详细教程参见BeautifulSoup中文文档:http://www.crummy.com/software/BeautifulSoup/bs3/documentation.zh.html
1)、包含包:import bs4
2)、读入:
 
[python] view plaincopyprint?


  • req = urllib2.Request(url)    
  • content = urllib2.urlopen(req).read()    
  • content = bs4.BeautifulSoup(content,from_encoding='GB18030')   

  
3)、查找内容
 
 
a、按html标签名查找:
  frameurl = content.findAll('frame')
framurl为存储所有frame标签内容的列表,例如frame[0] 为 tops.htm">
 
b、按标签属性查找
  frameurl = content.findAll(target=True)
查找所有含target属性的标签
frameurl = content.findAll(target=‘m_rbottom’)
查找所有含target属性且值为'm_rbottom'的标签
 
c、带有正则表达式的查找
  rawlv2 = content.findAll(href=re.compile(r'.htm$'))
查找所有含href属性且值为以'.htm'结尾的标签
 
d、综合查找
  frameurl = content.findAll('frame',target=‘rtop’)
 
查找所有frame标签,且target属性值为'rtop'
 
4)、访问标签属性值和内容
a、访问标签属性值
[python] view plaincopyprint?


  • rawlv2 = content.findAll(href=re.compile(r'.htm$'))    
  • href = rawlv2['href']  

  通过[属性名]即可访问属性值,如上式返回的便是href属性的值
b)、访问标签内容
[python] view plaincopyprint?


  • rawlv3 = content.findAll(href=re.compile(r'.mid$'))    
  • songname = str(rawlv3.text)  

  
上式访问了(内容)标签的实际内容,由于text为unicode类型,所以需要用str()做转换
 
 
附上最终的成果,程序功能是抓取www.dugukeji.com上的所有midi文件并下载,需要先建立./midi/dugukeji/文件夹和./midi/linklist文件
[python] view plaincopyprint?


  • "font-size:14px;">#-*- coding:utf-8 -*-   #允许文档中有中文  
  • import urllib2,urllib,cookielib,threading  
  • import os  
  • import re  
  • import bs4  
  • import sys  
  • reload(sys)  
  • sys.setdefaultencoding('utf-8'#允许打印unicode字符  
  •   
  •   
  • indexurl = 'http://www.dugukeji.com/'  
  • databasepath = './midi/linklist'  
  • path = './midi/dugukeji/'  
  • totalresult = {}  
  • oriresult = {}  
  •   
  • def crawl(url):  
  •     headers = { #伪装为浏览器抓取  
  •         'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'  
  •     }  
  •     req = urllib2.Request(url,headers=headers)  
  •     content = urllib2.urlopen(req).read()  
  •     content = bs4.BeautifulSoup(content,from_encoding='GB18030')  
  •     return content  
  •   
  •   
  • def crawlframe(sourceurl,target):  
  •     global indexurl  
  •     content = crawl(sourceurl)  
  •     #match = re.compile(r'(?<=target=["]' target '["] src=["]).*?(?=["])')   #正则表达式方法  
  •     #frameurl = re.findall(match,content)  
  •     frameurl = content.findAll('frame',target=target)   #beautifulsoup方法  
  •     result = indexurl frameurl[0]['src']  
  •     return result  
  •   
  • def crawllv1(frameurl,st=-1,en=-1):  
  •     global indexurl  
  •     content = crawl(frameurl)  
  •     #match = re.compile(r'(?<=href=["]).*?(?=["])')  
  •     #rawlv2 = re.findall(match,content)  
  •     rawlv2 = content.findAll(href=re.compile(r'.htm$'))  
  •     result = []  
  •     if st==-1 and en==-1:  
  •         for i in range(len(rawlv2)):  
  •             result.append(indexurl rawlv2['href'])  
  •     else:  
  •         for i in range(st,en):  
  •             result.append(indexurl rawlv2['href'])  
  •     #dele = []  
  •     #for i in range(len(result)):  
  •     #   if result[-4:]!='.htm' and result[-5:]!='.html':  
  •     #       dele.append(i)  
  • #       else:  
  • #           result=indexurl result  
  • #   if len(dele)>0:  
  • #       for deli in dele:  
  • #           del result[deli]  
  •   
  •     #result.sort()  
  •     return result  
  •   
  • def crawllv2(lv2url):  
  •     global indexurl  
  •     content = crawl(lv2url)  
  •     #match = re.compile(r'(?<=href=["]\.\.\/).*?[">].*?(?=[<])')  
  •     #rawlv3 = re.findall(match,content)  
  •     rawlv3 = content.findAll(href=re.compile(r'[..].*?[0-9].htm|.mid$'))  
  •     #print rawlv3  
  •     result = {} #结果字典,key:链接,value:歌曲名  
  •     for i in range(len(rawlv3)):  
  •         tmp = str(rawlv3['href'])  
  •         #print tmp  
  •         link = indexurl   tmp[tmp.rfind('..') 3:]   #有多个'..',找到最后一个  
  •         songname = ''  
  •         if tmp[-4:]=='.htm':    #需要访问3级页  
  •             try:  
  •                 conlv3 = crawl(link)  
  •             except:  
  •                 print 'WARNING: visit lv3 url failed!\n'  
  •             else:  
  •                 rawlv4 = conlv3.findAll(href=re.compile(r'.mid$'))  
  •                 if not rawlv4:  #4级页没有.mid下载链接,略过  
  •                     continue  
  •                 else:  
  •                     tmp = str(rawlv4[0]['href'])  
  •                     link = indexurl   tmp[tmp.rfind('..') 3:]  
  •   
  •         songname = str(rawlv3.text)  #将unicode类型的text转化为string  
  •         #songname.decode('GBK')  
  •         #songname.encode('utf-8')  
  •         songname = songname.replace(' ','_')    #将songname中空格和换行转化为下划线  
  •         songname = songname.replace('\n','_')   #原来存在的链接,直接略过  
  •         if oriresult.has_key(link):  
  •             continue  
  •         if totalresult.has_key(link) and len(songname)#如果链接已保存且歌曲名长度比当前的长,略过  
  •             continue  
  •         else:  
  •             totalresult[link] = songname  
  •             result[link] = songname     #加入字典  
  •     #result.sort()  
  •     return result  
  •   
  • def download(totalresult):  
  •     for link in totalresult.keys():  
  •         filepath = path   totalresult[link]   '.mid'  
  •         print 'download: ',link,' -> ',filepath,'\n'  
  •         urllib.urlretrieve(link, filepath)  
  •   
  •   
  • def readdata(databasepath):  
  •     datafile = open(databasepath,'r')   #读数据文件  
  •     link = datafile.readline()  
  •     while link:  
  •         oriresult[link]=''  
  •         link = datafile.readline()  
  •     datafile.close()  
  •   
  • def writedata(databasepath):  
  •     datafile = open(databasepath,'a')   #追加打开数据文件,将新链接写入文件尾  
  •     for link in totalresult.keys():  
  •         datafile.write(link,'\n')  
  •     datafile.close()  
  •   
  • if __name__ == '__main__':  
  •     try:  
  •         readdata(databasepath)  #访问文件,记录已下载的链接  
  •     except:  
  •         print 'WARNING:read database file failed!\n'  
  •     else:  
  •         print 'There is ',len(oriresult),' links in database.\n'  
  •   
  •     try:  
  •         frameurl1 = crawlframe(indexurl,'rtop'#抓取主页中一级页url所在frame的url  
  •     except:  
  •         print 'WARNING: crawl lv1 frameurl failed!\n'  
  •     try:  
  •         urllv1 = crawllv1(frameurl1,4,20)       #抓取一级页url  
  •     except:  
  •         print 'WARNING: crawl lv1 url failed!\n'  
  •   
  •     for i in urllv1:  
  •         print 'lv1 url:',i  
  •         try:  
  •             frameurl2 = crawlframe(i,'rbottom'#抓取一级页中二级页url所在frame的url  
  •         except:  
  •             print 'WARNING: crawl lv2 frameurl failed!\n'  
  •         else:  
  •             print '\tlv2 frameurl:',frameurl2  
  •             try:  
  •                 urllv2 = crawllv1(frameurl2)    #抓取二级页url  
  •             except:  
  •                 print 'WARNING: crawl lv2 url failed!\n'  
  •             else:  
  •                 for j in urllv2:  
  •                     print '\t\tlv2 url:',j  
  •                     try:  
  •                         urllv3 = crawllv2(j)  
  •                     except:  
  •                         print 'WARNING: crawl lv3 url failed!\n'  
  •                     else:  
  •                         for k in urllv3.keys():  
  •                             print '\t\t\tlv3 url:',k,'\tname:',urllv3[k]  
  •                         #download(urllv3)  
  •                               
  •     print 'new added midi num:',len(totalresult)  
  •     print '\nbegin to download...\n'  
  •     download(totalresult)  
  •     print '\nWrite database...\n'  
  •     writedata(databasepath)  
  •     print '\n\nDone!\n'  
  •   
  • """ 
  • url = 'http://www.dugukeji.com/' 
  • req = urllib2.Request(url) 
  • response = urllib2.urlopen(req).read() 
  • response = unicode(response,'GBK').encode('UTF-8') 
  • print response 
  • """  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-371119-1-1.html 上篇帖子: 零基础学python-3.1 python基本规则和语句 下篇帖子: Java程序员眼中的Python特征
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表