Python抓取网页&批量下载文件方法初探(正则表达式+BeautifulSoup) (转)

Python抓取网页&批量下载文件方法初探(正则表达式+BeautifulSoup)

最近两周都在学习Python抓取网页方法,任务是批量下载网站上的文件。对于一个刚刚入门python的人来说,在很多细节上都有需要注意的地方,以下就分享一下我在初学python过程中遇到的问题及解决方法。

一、用Python抓取网页

基本方法:

[python] view plaincopyprint?

  1. import urllib2,urllib
  2. url = ‘http://www.baidu.com‘
  3. req = urllib2.Request(url)
  4. content = urllib2.urlopen(req).read()

1)、url为网址,需要加‘http://‘

2)、content为网页的html源码

问题:

1、网站禁止爬虫,不能抓取或者抓取一定数量后封ip

解决:伪装成浏览器进行抓取,加入headers:

[python] view plaincopyprint?

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

更复杂的情况(需要登录,多线程抓取)可参考:http://www.pythonclub.org/python-network-application/observer-spider,很不错的教程

2、抓取网页中的中文为乱码问题

解决:用BeautifulSoup解析网页(BeautifulSoup是Python的一个用于解析网页的插件,其安装及使用方法下文会单独讨论)

首先需要介绍一下网页中的中文编码方式,一般网页的编码会在<meta>标签中标出,目前有三种,分别是GB2312,GBK,GB18030,三种编码是兼容的,

从包含的中文字符个数比较:GB2312 < GBK < GB18030,因此如果网页标称的编码为GB2312,但是实际上用到了GBK或者GB18030的中文字符,那么编码工具就会解析错误,导致编码退回到最基本的windows-2152了。所以解决此类问题分两种情况。

1)、若网页的实际的中文编码和其标出的相符的话,即没有字符超出所标称的编码,下面即可解决

[python] view plaincopyprint?

  1. import urllib,urllib2,bs4
  2. req = urllib2.Request(url)
  3. content = urllib2.urlopen(req).read()
  4. content = bs4.BeautifulSoup(content)
  5. return content

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

  1. import urllib,urllib2,bs4
  2. req = urllib2.Request(url)
  3. content = urllib2.urlopen(req).read()
  4. content = bs4.BeautifulSoup(content,from_encoding=‘GB18030‘)
  5. return content

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

二、用Python下载文件

使用Python下载文件的方法有很多,在此只介绍最简单的一种

[python] view plaincopyprint?

  1. import urllib
  2. urllib.urlretrieve(url, filepath)

url为下载链接,filepath即为存放的文件路径+文件名

更多Python下载文件方法参见:http://outofmemory.cn/code-snippet/83/sanzhong-Python-xiazai-url-save-file-code

三、使用正则表达式分析网页

将网页源码抓取下来后,就需要分析网页,过滤出要用到的字段信息,通常的方法是用正则表达式分析网页,一个例子如下:

[python] view plaincopyprint?

  1. import re
  2. content = ‘<a href="http://www.baidu.com">‘
  3. match = re.compile(r‘(?<=href=["]).*?(?=["])‘)
  4. 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%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97

个人推荐第一篇,条理清晰,不重不漏

在此就不赘述正则表达式的学习,只总结一下我在实际写正则时的认为需要注意的几个问题:

1)、一定要使用非贪婪模式进行匹配,即*?,+?(后加?),因为Python默认使用贪婪模式进行匹配,例如‘a.*b‘,它会匹配文档中从第一个a和最后一个b之间的文本,也就是说如果遇到一个b,它不会停止,会一直搜索至文档末尾,直到它确认找到的b是最后一个。而一般我们只想取某个字段的值,贪婪模式既不能返回正确的结果,还大大浪费了时间,所以非贪婪是必不可少的

2)、raw字符串的使用:如果要匹配一个.,*这种元字符,就需要加‘\‘进行转义,即要表示一个‘\‘,正则表达式需要多加一个转义,写成‘\\‘,但是Python字符串又需要对其转义,最终变成re.compile(‘\\\\‘),这样就不易理解且很乱,使用raw字符串让正则表达式变得易读,即写成re.compile(r‘\\‘),另一个方法就是将字符放到字符集中,即[\],效果相同

3)、()特殊构造的使用:一般来说,()中的匹配模式作为分组并可以通过标号访问,但是有一些特殊构造为例外,它们适用的情况是:我想要匹配href="xxxx"这个模式,但是我只需要xxxx的内容,而不需要前后匹配的模式,这时就可以用特殊构造(?<=),和(?=)来匹配前后文,匹配后不返回()中的内容,刚才的例子便用到了这两个构造。

4)、逻辑符的使用:如果想匹配多个模式,使用‘|‘来实现,比如

[python] view plaincopyprint?

  1. re.compile(r‘.htm|.mid$‘)

匹配的就是以.htm或.mid结尾的模式,注意没有‘&‘逻辑运算符

四、使用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?

  1. req = urllib2.Request(url)
  2. content = urllib2.urlopen(req).read()
  3. content = bs4.BeautifulSoup(content,from_encoding=‘GB18030‘)

3)、查找内容

a、按html标签名查找:

[python] view plaincopyprint?

  1. frameurl = content.findAll(‘frame‘)

framurl为存储所有frame标签内容的列表,例如frame[0] 为 <framename="m_rtop"
target="m_rbottom"src="tops.htm">

b、按标签属性查找

[python] view plaincopyprint?

  1. frameurl = content.findAll(target=True)

查找所有含target属性的标签

[python] view plaincopyprint?

  1. frameurl = content.findAll(target=‘m_rbottom’)

查找所有含target属性且值为‘m_rbottom‘的标签

c、带有正则表达式的查找

[python] view plaincopyprint?

  1. rawlv2 = content.findAll(href=re.compile(r‘.htm$‘))

查找所有含href属性且值为以‘.htm‘结尾的标签

d、综合查找

[python] view plaincopyprint?

  1. frameurl = content.findAll(‘frame‘,target=‘rtop’)

查找所有frame标签,且target属性值为‘rtop‘

4)、访问标签属性值和内容

a、访问标签属性值

[python] view plaincopyprint?

  1. <span style="font-size:14px;">rawlv2 = content.findAll(href=re.compile(r‘.htm$‘))
  2. href = rawlv2[i][‘href‘]</span>

通过[属性名]即可访问属性值,如上式返回的便是href属性的值

b)、访问标签内容

[python] view plaincopyprint?

  1. rawlv3 = content.findAll(href=re.compile(r‘.mid$‘))
  2. songname = str(rawlv3[i].text)

上式访问了<a href=...>(内容)</a>标签的实际内容,由于text为unicode类型,所以需要用str()做转换

附上最终的成果,程序功能是抓取www.dugukeji.com上的所有midi文件并下载,需要先建立./midi/dugukeji/文件夹和./midi/linklist文件

[python] view plaincopyprint?

  1. #-*- coding:utf-8 -*- #允许文档中有中文
  2. import urllib2,urllib,cookielib,threading
  3. import os
  4. import re
  5. import bs4
  6. import sys
  7. reload(sys)
  8. sys.setdefaultencoding(‘utf-8‘) #允许打印unicode字符
  9. indexurl = ‘http://www.dugukeji.com/‘
  10. databasepath = ‘./midi/linklist‘
  11. path = ‘./midi/dugukeji/‘
  12. totalresult = {}
  13. oriresult = {}
  14. def crawl(url):
  15. headers = { #伪装为浏览器抓取
  16. ‘User-Agent‘:‘Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6‘
  17. }
  18. req = urllib2.Request(url,headers=headers)
  19. content = urllib2.urlopen(req).read()
  20. content = bs4.BeautifulSoup(content,from_encoding=‘GB18030‘)
  21. return content
  22. def crawlframe(sourceurl,target):
  23. global indexurl
  24. content = crawl(sourceurl)
  25. #match = re.compile(r‘(?<=target=["]‘+target+‘["] src=["]).*?(?=["])‘)   #正则表达式方法
  26. #frameurl = re.findall(match,content)
  27. frameurl = content.findAll(‘frame‘,target=target)   #beautifulsoup方法
  28. result = indexurl+frameurl[0][‘src‘]
  29. return result
  30. def crawllv1(frameurl,st=-1,en=-1):
  31. global indexurl
  32. content = crawl(frameurl)
  33. #match = re.compile(r‘(?<=href=["]).*?(?=["])‘)
  34. #rawlv2 = re.findall(match,content)
  35. rawlv2 = content.findAll(href=re.compile(r‘.htm$‘))
  36. result = []
  37. if st==-1 and en==-1:
  38. for i in range(len(rawlv2)):
  39. result.append(indexurl+rawlv2[i][‘href‘])
  40. else:
  41. for i in range(st,en):
  42. result.append(indexurl+rawlv2[i][‘href‘])
  43. #dele = []
  44. #for i in range(len(result)):
  45. #   if result[i][-4:]!=‘.htm‘ and result[i][-5:]!=‘.html‘:
  46. #       dele.append(i)
  47. #       else:
  48. #           result[i]=indexurl+result[i]
  49. #   if len(dele)>0:
  50. #       for deli in dele:
  51. #           del result[deli]
  52. #result.sort()
  53. return result
  54. def crawllv2(lv2url):
  55. global indexurl
  56. content = crawl(lv2url)
  57. #match = re.compile(r‘(?<=href=["]\.\.\/).*?[">].*?(?=[<])‘)
  58. #rawlv3 = re.findall(match,content)
  59. rawlv3 = content.findAll(href=re.compile(r‘[..].*?[0-9].htm|.mid$‘))
  60. #print rawlv3
  61. result = {} #结果字典,key:链接,value:歌曲名
  62. for i in range(len(rawlv3)):
  63. tmp = str(rawlv3[i][‘href‘])
  64. #print tmp
  65. link = indexurl + tmp[tmp.rfind(‘..‘)+3:]   #有多个‘..‘,找到最后一个
  66. songname = ‘‘
  67. if tmp[-4:]==‘.htm‘:    #需要访问3级页
  68. try:
  69. conlv3 = crawl(link)
  70. except:
  71. print ‘WARNING: visit lv3 url failed!\n‘
  72. else:
  73. rawlv4 = conlv3.findAll(href=re.compile(r‘.mid$‘))
  74. if not rawlv4:  #4级页没有.mid下载链接,略过
  75. continue
  76. else:
  77. tmp = str(rawlv4[0][‘href‘])
  78. link = indexurl + tmp[tmp.rfind(‘..‘)+3:]
  79. songname = str(rawlv3[i].text)  #将unicode类型的text转化为string
  80. #songname.decode(‘GBK‘)
  81. #songname.encode(‘utf-8‘)
  82. songname = songname.replace(‘ ‘,‘_‘)    #将songname中空格和换行转化为下划线
  83. songname = songname.replace(‘\n‘,‘_‘)   #原来存在的链接,直接略过
  84. if oriresult.has_key(link):
  85. continue
  86. if totalresult.has_key(link) and len(songname)<len(totalresult[link]):   #如果链接已保存且歌曲名长度比当前的长,略过
  87. continue
  88. else:
  89. totalresult[link] = songname
  90. result[link] = songname     #加入字典
  91. #result.sort()
  92. return result
  93. def download(totalresult):
  94. for link in totalresult.keys():
  95. filepath = path + totalresult[link] + ‘.mid‘
  96. print ‘download: ‘,link,‘ -> ‘,filepath,‘\n‘
  97. urllib.urlretrieve(link, filepath)
  98. def readdata(databasepath):
  99. datafile = open(databasepath,‘r‘)   #读数据文件
  100. link = datafile.readline()
  101. while link:
  102. oriresult[link]=‘‘
  103. link = datafile.readline()
  104. datafile.close()
  105. def writedata(databasepath):
  106. datafile = open(databasepath,‘a‘)   #追加打开数据文件,将新链接写入文件尾
  107. for link in totalresult.keys():
  108. datafile.write(link,‘\n‘)
  109. datafile.close()
  110. if __name__ == ‘__main__‘:
  111. try:
  112. readdata(databasepath)  #访问文件,记录已下载的链接
  113. except:
  114. print ‘WARNING:read database file failed!\n‘
  115. else:
  116. print ‘There is ‘,len(oriresult),‘ links in database.\n‘
  117. try:
  118. frameurl1 = crawlframe(indexurl,‘rtop‘) #抓取主页中一级页url所在frame的url
  119. except:
  120. print ‘WARNING: crawl lv1 frameurl failed!\n‘
  121. try:
  122. urllv1 = crawllv1(frameurl1,4,20)       #抓取一级页url
  123. except:
  124. print ‘WARNING: crawl lv1 url failed!\n‘
  125. for i in urllv1:
  126. print ‘lv1 url:‘,i
  127. try:
  128. frameurl2 = crawlframe(i,‘rbottom‘) #抓取一级页中二级页url所在frame的url
  129. except:
  130. print ‘WARNING: crawl lv2 frameurl failed!\n‘
  131. else:
  132. print ‘\tlv2 frameurl:‘,frameurl2
  133. try:
  134. urllv2 = crawllv1(frameurl2)    #抓取二级页url
  135. except:
  136. print ‘WARNING: crawl lv2 url failed!\n‘
  137. else:
  138. for j in urllv2:
  139. print ‘\t\tlv2 url:‘,j
  140. try:
  141. urllv3 = crawllv2(j)
  142. except:
  143. print ‘WARNING: crawl lv3 url failed!\n‘
  144. else:
  145. for k in urllv3.keys():
  146. print ‘\t\t\tlv3 url:‘,k,‘\tname:‘,urllv3[k]
  147. #download(urllv3)
  148. print ‘new added midi num:‘,len(totalresult)
  149. print ‘\nbegin to download...\n‘
  150. download(totalresult)
  151. print ‘\nWrite database...\n‘
  152. writedata(databasepath)
  153. print ‘\n\nDone!\n‘
  154. """
  155. url = ‘http://www.dugukeji.com/‘
  156. req = urllib2.Request(url)
  157. response = urllib2.urlopen(req).read()
  158. response = unicode(response,‘GBK‘).encode(‘UTF-8‘)
  159. print response
  160. """

人人网
开心网
搜狐微博
    推荐给朋友

    举报

时间: 2024-10-01 09:51:31

Python抓取网页&批量下载文件方法初探(正则表达式+BeautifulSoup) (转)的相关文章

PHP利用Curl实现多线程抓取网页和下载文件

PHP 利用 Curl  可以完成各种传送文件操作,比如模拟浏览器发送GET,POST请求等等,然而因为php语言本身不支持多线程,所以开发爬虫程序效率并不高,一般采集 数据可以利用 PHPquery类来采集数据库,在此之外也可以用 Curl ,借助Curl 这个功能实现并发多线程的访问多个url地址以实现并发多线程抓取网页或者下载文件. 至于具体实现过程,请参考下面几个例子: 1.实现抓取多个URL并将内容写入指定的文件 $urls = array( '路径地址', '路径地址', '路径地址

Python爬取网页的三种方法

# Python爬取网页的三种方法之一:  使用urllib或者urllib2模块的getparam方法 import urllib fopen1 = urllib.urlopen('http://www.baidu.com').info() fopen2 = urllib2.urlopen('http://www.sina.com').info() print fopen1.getparam('charset') print fopen2.getparam('charset') #----有些

&lt;&lt;&lt; JS实现网页批量下载文件,支持PC/手机

//把下载链接放入集合里 var downloadData = new Array{"http://www.empli.com/data1.apk","http://www.empli.com/data1.apk","http://www.empli.com/data1.apk","http://www.empli.com/data1.apk"}; var downloadNum=0;//方法执行次数 circularWind

python抓取网页

一 不需要登录的 import urllib2 content = urllib2.urlopen('http://XXXX').read() 二 使用代理 import urllib2 proxy_support = urllib2.ProxyHandler({'http':'http://XX.XX.XX.XX:XXXX'}) opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler) urllib2.install_o

使用python抓取并分析数据—链家网(requests+BeautifulSoup)(转)

本篇文章是使用python抓取数据的第一篇,使用requests+BeautifulSoup的方法对页面进行抓取和数据提取.通过使用requests库对链家网二手房列表页进行抓取,通过BeautifulSoup对页面进行解析,并从中获取房源价格,面积,户型和关注度的数据. 准备工作 首先是开始抓取前准备工作,导入需要使用的库文件,这里主要使用的是requests和BeautifulSoup两个.Time库负责设置每次抓取的休息时间.这里并非全部,后续还会在过程中导入新的库. 抓取列表页 开始抓取

使用HttpWebRequest和HtmlAgilityPack抓取网页(拒绝乱码,拒绝正则表达式)

废话不多说, 直接说需求. 公司的网站需要抓取其他网站的文章,但任务没到我这,同事搞了一下午没搞出来.由于刚刚到公司, 想证明下自己,就把活揽过来了.因为以前做过,觉得应该很简单,但当我开始做的时候,我崩溃了,http请求后,得到的是字符串竟然是乱码,然后就各种百度(谷歌一直崩溃中),最后找到了原因.由于我要抓取的网页做了压缩,所以当我抓的时候,抓过来的是压缩后的,所以必须解压一下,如果不解压,不管用什么编码方式,结果还是乱码.直接上代码: 1 public Encoding GetEncodi

Python抓取网页中的图片到本地

今天在网上找了个从网页中通过图片URL,抓取图片并保存到本地的例子: 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 # Author: xixihuang 5 # Date : 2016/08/28 10:12 AM 6 # Desc: 抓取网页,获取图片URL,抓取图片内容并保存到本地. 7 8 import os 9 import uuid 10 import urllib2 11 import cookielib 12 '''获取

浅谈如何使用python抓取网页中的动态数据

我们经常会发现网页中的许多数据并不是写死在HTML中的,而是通过js动态载入的.所以也就引出了什么是动态数据的概念, 动态数据在这里指的是网页中由Javascript动态生成的页面内容,是在页面加载到浏览器后动态生成的,而之前并没有的. 在编写爬虫进行网页数据抓取的时候,经常会遇到这种需要动态加载数据的HTML网页,如果还是直接从网页上抓取那么将无法获得任何数据. 今天,我们就在这里简单聊一聊如何用python来抓取页面中的JS动态加载的数据. 给出一个网页:豆瓣电影排行榜,其中的所有电影信息都

python抓取网页图片的小案例

1.分析 ,要抓取的页面的信息以及对应的源码信息 blog.sina.com.cn/s/blog 93dc666c0101b1bj.html 2.代码模块: 导入正则表达的模块 导入url相关的模块 利用page.read()获取页面的信息,再将页面当做字符串,传入到getImg方法内,利用正则表达式,匹配你要的字符串信息,然后,在利用urllib包的urlretrieve()下载,你要的图片. 这个是urllib模块内的方法的详解:http://www.jb51.net/article/426