网络爬虫

获取一个网页并格式化内容

方法一:

from urllib.request import urlopen
from urllib.error import HTTPError
from bs4 import BeautifulSoup

def getTitle(url):
    try:
        html = urlopen(url)  #获取一个网页
    except HTTPError as e:  #如果网页不存在,返回None
        return None

    try:
        bs = BeautifulSoup(html.read(),'html.parser')  #格式化传入的网页
        title = bs.body
    except AttributeError as e:  #如果想调用的标签不存在,返回None,此例中调用body标签
        return None
    return title  #返回格式化的网页内容

title = getTitle('http://ww.baidu.com')
if title == None:
    print('Title cound not be found')
else:
    print(title)

方法二:

import requests  #使用requests库
from bs4 import BeautifulSoup

def getHTMLText(url):
    try:
        r = requests.get(url, timeout=30)  #requests.get()函数
        r.raise_for_status()  #如果连接失败(不是200),产生异常requests.HTTPError
        r.encoding = r.apparent_encoding  #改变编码方式
        demo = r.text  #url对应的页面内容
    except:
        return None

    soup = BeautifulSoup(demo,'html.parser')  #格式化
    return soup.prettify()  #基于bs4库的.prettify()方法,令内容更加友好


url = 'http://www.baidu.com'
h = getHTMLText(url)
if h == None:  #
    print('web could not be found')
else:
    print(h)

BeautifulSoup的find()和find_all()

find_all(tag, attributes, recursive, text, limit, keyowrds)
find(tag, attributes, recursive, text, keywords)

· tag:传递一个/多个标签名组成列表做参数,限定作用的标签。
· attributes:标签属性参数,返回符合属性的标签
· recursive:布尔变量。设置为True:按要求查找所有子标签及子的子;设置为False:只查找文档的一级标签。
· text:用标签的文本内容去匹配。例:查找含某内容的标签数量

nameList = bs.find_all(text='the prince')
print(len(nameList))

· limit:获取网页中前x项结果
· keyword:选择具有指定属性的标签。例:

title = bs.find_all(id='title', class='text')

上述代码返回第一个在class_属性中包含单词text且在id属性中包含title的标签。

其他BeautifulSoup对象

BeautifulSoup对象

前面代码示例中的bs

标签Tag对象

例:bs.div.h1

NavigableString对象

表示标签里的文字

Comment对象

查找HTML文档的注释标签,<! - - 像这样 - - >

导航树

通过标签在文档中的位置来查找标签
1.处理子标签和其他后代标签
一般情况下,BeautifulSoup函数处理当前标签的后代标签。
例:
bs.body.h1:选择body标签后代里的第一个h1标签
bs.div.find_all("img"):找出文档中的第一个div标签,获得div后代里所有img标签的列表

找出子标签

from urllib.request import urlopen
from bs4 import BeautifulSoup

html = urlopen('http://www.pythonscraping.com/pages/page3.html')
bs = BeautifulSoup(html, 'html.parser')

for child in bs.find('table',{'id':'giftList'}).children:
    print(child)
#打印giftList表格中所有产品的数据行

2.处理兄弟标签
next_siblings()函数:调用后面的兄弟标签

平行标签

from urllib.request import urlopen
from bs4 import BeautifulSoup

html = urlopen('http://www.pythonscraping.com/pages/page3.html')
bs = BeautifulSoup(html,'html.parser')

for sibling in bs.find('table', {'id':'giftList'}).tr.next_siblings:
    print(sibling)
    
#打印产品表格里所有行的产品,第一行表格标题除外。

类似的道理,还有next_siblingprevious_siblingprevious_siblings函数

3.处理父标签
.parent或.parents

from urllib.request import urlopen
from bs4 import BeautifulSoup

html = ('http://www.pythonscraping.com/pages/page3.html')
bs = BeautifulSoup(html,'html.parser')
print(bs.find('img',{'src':'../img/gifts/img1.jpg'}).parent.previous_sibling.get_text())

正则表达式

用一系列线性规则构成的字符串:
1."a"至少出现一次
2.后面跟着"b",重复5次
3.后面再跟"c",重复任意偶数次
4.最后一位是"d"或"e"
则:aabbbbb(cc)(d|e)
aa:a表示重复任意次a,包括0次
(cc)*:重复任意次cc,包括0次
(d|e):增加一个d或一个e

常用的正则表达式符合

符合 含义 例子 匹配结果
* 匹配前面字符0次或多次 ab aaaaaa,aaabbb,bbbbbb
+ 匹配前面字符至少一次 a+b+ aaaaab,aaaabbb,abbbb
[ ] 匹配括号里的任意一个字符 [A-Z]* APPLE,CAPITALS
( ) 优先运行
{m,n} 匹配前面字符m到n次 a{2,3} aa,aaa
[^] 匹配任意一个不在[ ]里的字符 [^A-Z]* apple,mysql
| 匹配任意一个被|分割的字符 a|e a,e
. 匹配任意单个字符 b.d bad,bzd,b@d,b d
^ 字符串开始位置的字符或子表达式 ^a apple,asdf,a
\ 转义字符 \.\|\\ .|\
$ 经常用在正则表达式末尾,表示从字符串的末端匹配 [A-Z]*[a-z]*$ ABCabc,zzzyx,Bob
?! "不包含"。表示字符不能出现在目标字符串里。

经典应用:识别邮箱地址
规则:
1.大写字母,小写字母,数字,点号,加号,下划线:
[A-Za-z0-9.+]+
2.@:@
3.@之后至少包含一个大写\小写字母:[A-Za-z]+
4.点号:.
5.邮箱地址结尾:com|org|edu|net
答案:[A-Za-z0-9.
+]+@[A-Za-z]+.(com|org|edu|net)

正则表达式和BeautifulSoup

抓取网页:http://www.pythonscraping.com/pages/page3.html
上面几张图片的源代码:<img src="../img/gifts/img3.jpg">
如果想抓取所有图片的url链接,很直接的做法是find_all("img")抓取所有图片。但除目标图片外,现代网站里都有一些隐藏的图片、用于网页布局留白、元素对齐的空白图片。所以,我们直接通过商品图片的文件路径来查找:

from urllib.request import urlopen
from bs4 import BeautifulSoup
import re

html = urlopen('http://www.pythonscraping.com/pages/page3.html')
bs = BeautifulSoup(html,'html.parser')
images = bs.find_all('img',{'src':re.compile('\.\.\/img\/gifts\/img.*\.jpg')})
for image in images:
    print(image['src'])

获取属性

获取一个标签对象的全部属性:
myTag.attrs
例:获取图片的源位置src

myImgTag.attrs['src']

Lambda表达式

BeautifulSoup允许把特定类型的函数作为参数传入find_all函数。唯一的限制条件是这些函数必须把一个标签对象作为参数并且返回布尔类型的结果。
BeautifulSoup用这个函数来评估它遇到的每个标签对象,最后把评估结果为“真”的标签保留,把其他标签剔除。
例:获取有两个属性的所有标签:
bs.find_all(lambda tag: len(tag.attrs) == 2)
这里作为参数传入的函数是len(tag.attrs)==2。当参数为真时,find_all函数将返回tag。即找出带有两个属性的所有标签。
Lambda函数非常实用,你甚至可以用它来替代现有的BeautifulSoup函数:
bs.find_all(lambda tag: tag.get_text() == 'Or maybe he\'s only resting?')
不使用Lambda函数:
bs.find_all(' ', text = 'Or maybe he\'s only resting?')

提取页面中的链接

from urllib.request import urlopen
from bs4 import BeautifulSoup

html = urlopen('https://cn.bing.com')
bs = BeautifulSoup(html, 'html.parser')
for link in bs.find_all('a'):
    if 'href' in link.attrs:
        print(link.attrs['href'])

抓取整个网站

from urllib.request import urlopen
from bs4 import BeautifulSoup
import re

pages = set()
def getLinks(pageUrl):
    global pages
    html = urlopen('http://en.wikipedia.org{}'.format(pageUrl))
    bs = BeautifulSoup(html,'html.parser')
    for link in bs.find_all('a', href=re.compile('^(/wiki/)')):
        if 'href' in link.attrs:
            if link.attrs['href'] not in pages:
                #we have encountered a new page
                newPage = link.attrs['href']
                print(newPage)
                pages.add(newPage)
                getLinks(newPage)
getLinks('')

为避免一个页面被抓取两次,链接的去重是非常重要的。在代码运行时,要把已发现的所有链接都放到一起,并保存在方便查询的集合(set)里。集合中的元素没有特定的顺序,集合只存储唯一的元素。

规划和定义对象

1.思考自己需要什么信息?
2.这个信息有助于实现目的吗?是否抓取这个信息有什么影响?
3.晚些抓取会有什么影响?
4.是否冗余?
5.数据存储是否符合逻辑?

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • 网络爬虫提取 [TOC] 1. Beautiful Soup库入门 bs库的安装 win平台下,cmd输入 bea...
    icey_J阅读 783评论 0 1
  • 声明:本文讲解的实战内容,均仅用于学习交流,请勿用于任何商业用途! 一、前言 强烈建议:请在电脑的陪同下,阅读本文...
    Bruce_Szh阅读 12,679评论 6 28
  • 这是《Web Scraping with Python》一书的阅读笔记。该笔记跳过了一些不必要的描述,对书的代码也...
    浮云匿晨晖阅读 1,766评论 1 5
  • 今天学习了刘润老师的商学院5分钟时间颗粒度,第一我就明白了为什么我不是老板!因为我每天对时间就没有度!!! 今天舍...
    燕子_e711阅读 196评论 0 0
  • 昨晚,康熙来了最后一夜播出,开场平和,没有以往节目的吵闹。康永一段长长的向观众向小s向工作人员告白的开场白,引出了...
    遇见青丘阅读 428评论 0 0