Ch3 我的第一个爬虫程序

概要


了解了爬虫原理和网页构造后,我们知道了爬虫的任务就是两件事:请求网页和解析提取信息。

主要涉及的知识点如下:

  • Python第三方库:学会Python第三方库的概念及安装方法
  • Requests库:学会Requests库的使用原理和方法
  • BeautifulSoup库:学会BeautifulSoup库的使用原理和方法
  • Requests 和 BeautifulSoup库组合使用:通过最后给出的实例,演示如何利用这两大库进行爬虫的方法和技巧。

正文


1. Python第三方库

1.1 Python第三方库的概念

Python之所以强大并逐渐流行起来,一部分原因要归功于Python强大的第三方库。这样用户就不用了解底层的思想,用最少的代码写出最多的功能。

就像制造自行车一样,需要:

  • 铁矿石
  • 橡胶
  • 扳手等工具

如果没使用第三方库的话,就需要从原始材料开始,一步步制造下去,一个车轮子都不知道要造多久呢!而使用第三方库就不一样了,车轮子和车身都已经制造好了,拼接一下就可以使用了。这种拿来即用的就是Python第三方库。

1.2 Python第三方库的安装方法

安装步骤以Windows 7 系统为准

在Pycharm中安装

  • 打开Pycharm,在菜单栏中选择File|Default Settings命令。

  • 在弹出的对话框中选择左侧的Project Interpreter选项,在窗口右方选择Python环境。

  • 单击加号按钮添加第三方库。

  • 输入第三方库名称,选中需下载的库。

  • 勾选Install to users site复选框,然后单击Install Package按钮。

在安装完成后,Pycharm会有成功提示。

1.3 Python第三方库的使用方法

当成功安装Python第三方库后,就可通过下面的方法导入并使用第三方库了。

import xxxx
#xxxx为导入的库名,例如import requests

注意: 导入BeautifulSoup库的写法为 from bs4 import BeautifulSoup


2. 爬虫三大库

讲过了Requests、Lxml和BeautifulSoup库的安装后,将依次讲解各个库的说明和使用方法。

2.1 Requests库

Requests库的官方文档指出,让HTTP服务人类。Requests库的作用就是请求网站获取网页数据的,让我们从简单的实例开始,讲解Requests库的使用方法。

import requests

res = requests.get("http://bj.xiaozhu.com/")
print(res)
#<Response [200]>,说明请求网址成功。
print()
print(res.text)
#如下图所示结果

这时,打开Chrome浏览器,进入http://bj.xiaozhu.com/,在空白处右击,在弹出的快捷菜单中选择“查看网页源代码”命令,可以看到代码返回的结果就是网页的源代码。

有时爬虫需要加入请求头来伪装成浏览器,以便更好地抓取数据。在Chrome浏览器中按F12键打开Chrome开发者工具,刷新网页后找到User-Agent进行复制,如下图:

复制请求头
import requests

headers = {
   'User-Agent': 'Mozilla/5.0 '
                 '(Windows NT 6.1; Win64; x64) '
                 'AppleWebKit/537.36 (KHTML, like Gecko) '
                 'Chrome/69.0.3497.100 Safari/537.36'}
res = requests.get("http://bj.xiaozhu.com/", headers=headers)
# get方法加入请求头
print(res)

print()
print(res.text)

Requests库不仅有get()方法,还有post()等方法。post()方法用于提交表单来爬取需要登录才能获得数据的网站。学习get()方法足够我们爬取大部分的网站了。

Requests库请求并不会“一帆风顺”,当遇到一些情况时,Requests库会抛出错误或者异常,Requests库的错误和异常主要有以下4种。

  • Requests抛出一个ConnectionError异常,原因为网络问题(如DNS查询失败、拒绝连接等)

  • Response.raise_for_status()抛出一个HTTPError异常,原因为HTTP请求返回了不成功的状态码(如网页不存在,返回404错误)

  • Requests抛出一个Timeout异常,原因为请求超时。

  • Requests抛出一个TooManyRedirects异常,原因为请求超过了设定的最大重定向次数。

所有Requests显示抛出的异常都继承自requests.exceptions.RequestException,当发现这些错误或异常进行代码修改重新再来时,爬虫的程序又开始重新运行了,爬取到的数据又会重新爬取一次,这对于爬虫的效率和质量来说都是不利的。这时,便可通过Python中的try来避免异常了,具体使用方法如下:

import requests

headers = {
    'User-Agent': 'Mozilla/5.0 '
                  '(Windows NT 6.1; Win64; x64) '
                  'AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/69.0.3497.100 Safari/537.36'}
res = requests.get("http://bj.xiaozhu.com/", headers=headers)
# get方法加入请求头
try:
    print(res.text)
except ConnectionError:
    print("拒绝连接")

通过try和except,如果请求成功了,会打印网页的源代码。如果请求出现了ConnectionError异常,则会打印“拒绝连接”,这样程序就不会报错,而是给编程者一个提示,不会影响下面代码的运行。

2.2 BeautifulSoup库

BeautifulSoup库是一个非常流行的Python模块。通过BeautifulSoup库可以轻松地解析Requests库请求的网页,并把网页源代码解析成Soup文档,以便过滤提取数据。

import requests
from bs4 import BeautifulSoup

headers = {
    'User-Agent': 'Mozilla/5.0 '
                  '(Windows NT 6.1; Win64; x64) '
                  'AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/69.0.3497.100 Safari/537.36'}
res = requests.get("http://bj.xiaozhu.com/", headers=headers)

soup = BeautifulSoup(res.text, 'html.parser')
# 对返回的结果进行解析
print(soup.prettify())

输出的结果如下图所示:

看上去与Requests库请求返回的网页源代码类似,但通过BeautifulSoup库解析得到的Soup文档按照标准缩进格式的结构输出,为结构化的数据,为数据的过滤提取做好准备。

BeautifulSoup库除了支持Python标准库中的HTML解析器外,还支持一些第三方的解析器。

如下列出了BeautifulSoup库的主要解析器及相应的优缺点。

解析器 使用方法 优点 缺点
Python标准库 BeautifulSoup(markup,"html.parser") Python的内置标准库执行速度适中,文档容错能力强 Python2.7.3 or Python 3.2.2前的版本中文档容错能力差
lxml HTML解析器 BeautifulSoup(markup,"lxml") 速度快,文档容错能力强 需要安装C语言库
Lxml XML解析器 BeautifulSoup(markup,["lxml","xml"]) BeautifulSoup(markup,"xml") 速度快,唯一支持XML的解析器 需要安装C语言库
html5lib BeautifulSoup(markup,"html5lib") 最好的容错性,以浏览器的方式解析文档,生成HTML5格式的文档 速度慢,不依赖外部扩展

注意: BeautifulSoup库官方推荐使用lxml作为解析器,因为效率更高。

解析得到的Soup文档可以使用find()find_all()方法及selector()方法定位需要的元素了。

find()find_all()两个方法用法相似,BeautifulSoup文档中对这两个方法的定义是这样的:

find_all(name=None, attrs={}, recursive=True, text=None,limit=None, **kwargs)
find(name=None, attrs={}, recursive=True, text=None,**kwargs):

常用的是前两个参数,熟练运用这两个参数,就可以提取出想要的网页信息。

1.find_all()方法

soup.find_all('div', 'item')
# 查找div标签,class="item"
res = soup.find_all('div', attrs={"class": "item"})
# attrs参数定义一个字典参数来搜索包含特殊属性的tag

2.find()方法

find()方法与find_all()方法类似,只是
find_all()方法返回的是文档中符合条件的所有Tag,是一个集合(class 'bs4.element.ResultSet')
find()方法返回的是一个Tag(class 'bs4.element.Tag')

3.selector()方法

soup.select(div.item > a > h1)
# 括号内容通过Chrome复制得到

该方法类似于中国>江苏省>泰兴市,从大到小,提取需要的信息,这种方式可以通过Chrome复制得到,如下图:

复制select

(1)鼠标定位到想要提取的数据位置,右击,在弹出的快捷菜单中选择“检查”命令。

(2)在网页源代码中右击所选元素

(3)在弹出的快捷菜单中选择Copy selector。这时便能得到:

#page_list > ul > li:nth-child(1) > div.result_btm_con.lodgeunitname > span.result_price > i

通过代码即可得到房子价格:

import requests
from bs4 import BeautifulSoup

headers = {
    'User-Agent': 'Mozilla/5.0 '
                  '(Windows NT 6.1; Win64; x64) '
                  'AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/69.0.3497.100 Safari/537.36'}
res = requests.get("http://bj.xiaozhu.com/", headers=headers)
soup = BeautifulSoup(res.text, 'html.parser')

res = soup.select('#page_list > ul > li:nth-child(1) > div.result_btm_con.lodgeunitname > span.result_price > i')
# 定位元素位置并通过select方法提取

print(res)

注意:li:nth-child(1)在Python中运行会报错,需改为li:nth-of-type(1)

此时的li:nth-of-type(1)为选择的一个价格,为了做短租房的平均价格分析,当然是要把所有的房租价格全部提取出来。把selector改为:

# page_list > ul > li > div.result_btm_con.lodgeunitname > span.result_price > i

就可以得到整个页面的所有价格,这样提取的信息为列表,可以通过循环分别打印出来也可以存储起来。

import requests
from bs4 import BeautifulSoup

headers = {
    'User-Agent': 'Mozilla/5.0 '
                  '(Windows NT 6.1; Win64; x64) '
                  'AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/69.0.3497.100 Safari/537.36'}
res = requests.get("http://bj.xiaozhu.com/", headers=headers)
soup = BeautifulSoup(res.text, 'html.parser')

prices = soup.select('#page_list > ul > li > div.result_btm_con.lodgeunitname > span.result_price > i')
# 此时prices为列表,需循环遍历

for price in prices:
    print(price)
代码运行结果

读者会发现,提取的信息为<i>328</i>这种标签,而我们只需要其中的数据,这时用get_text()方法即可获得中间的文字信息。

import requests
from bs4 import BeautifulSoup

headers = {
    'User-Agent': 'Mozilla/5.0 '
                  '(Windows NT 6.1; Win64; x64) '
                  'AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/69.0.3497.100 Safari/537.36'}
res = requests.get("http://bj.xiaozhu.com/", headers=headers)
soup = BeautifulSoup(res.text, 'html.parser')

prices = soup.select('#page_list > ul > li > div.result_btm_con.lodgeunitname > span.result_price > i')
# 此时prices为列表,需循环遍历

for price in prices:
    print(price.get_text())

这时程序就已经爬取了一页中所有的房价信息,这时就需要构造URL列表。

2.3 Lxml库

Lxml库是基于libxml2这一个XML解析库的Python封装。该模块使用C语言编写,解析速度比BeautifulSoup更快,具体在后面讲到。


3.综合案例1----爬取北京地区短租房信息

利用Requests和BeautifulSoup第三方库,爬取小猪短租网北京地区租房的信息。

3.1爬虫思路分析

(1)爬取小猪短租网北京地区短租房13页的信息。通过手动浏览,以下为前4页的网址:

http://bj.xiaozhu.com
http://bj.xiaozhu.com/search-duanzufang-p2-0/
http://bj.xiaozhu.com/search-duanzufang-p3-0/
http://bj.xiaozhu.com/search-duanzufang-p4-0/

然后把第一页的网址改为http://bj.xiaozhu.com/search-duanzufang-p1-0/后也能正常浏览,因此只需更p后面的数字即可,以此来构造出13页的网址。

(2)本次爬虫在详细页面中进行,因此需要先爬取进入详细页面的网址链接,进而爬取数据。

(3)需要爬取的信息有:标题,地址,价格,房东名称,房东性别和房东头像的链接。如下图:

需获取的网页信息

3.2 爬虫代码及分析

爬虫代码如下:

import requests
from bs4 import BeautifulSoup
import time

# 导入相应的库文件

# 加入请求头
headers = {
    'User-Agent': 'Mozilla/5.0 '
                  '(Windows NT 6.1; Win64; x64) '
                  'AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/69.0.3497.100 Safari/537.36'}


def judgment_sex(class_name):
    # 定义判断用户性别的函数
    if class_name == 'member_ico1':
        return '女'
    else:
        return '男'


def get_links(url):
    # 定义获取详细页URL的函数
    wb_data = requests.get(url, headers=headers)
    soup = BeautifulSoup(wb_data.text, "lxml")

    links = soup.select("#page_list > ul > li > a")

    # links为URL列表
    for link in links:
        href = link.get("href")
        # print(href)
        get_info(href)
        time.sleep(0.5)


def get_info(url):
    wb_data = requests.get(url, headers=headers)

    soup = BeautifulSoup(wb_data.text, "lxml")

    titles = soup.select("div.pho_info > h4 ")

    # print(titles[0].get_text().strip())

    addresses = soup.select('div.pho_info > p > span')

    # print(addresses[0].get_text())

    prices = soup.select('div.day_l > span')

    # print(prices[0].get_text())

    imgs = soup.select('div.member_pic > a > img')

    # print(imgs[0].get('src'))

    names = soup.select('div.w_240 > h6 > a')

    # print(names[0].get_text())

    sexs = soup.select('div.member_pic > div')

    # print(sexs[0].get('class')[0])
    # 性别返回的是两种结果:member_ico,member_ico1

    for title, address, price, img, name, sex in zip(titles, addresses, prices, imgs, names, sexs):
        # print(title.get_text())
        # print(address)
        # print(price)
        # print(img)
        # print(name)
        # print(sex)
        data = {'title': title.get_text().strip(),
                'address': address.get_text().strip(),
                'price': price.get_text(),
                'img': img.get('src'),
                'name': name.get_text(),
                'sex': judgment_sex(sex.get('class')[0])
                }

        print(data)


# get_links("http://bj.xiaozhu.com/")
# get_info('http://bj.xiaozhu.com/fangzi/31531500903.html')

if __name__ == '__main__':
    # 为程序的主入口
    urls = ['http://bj.xiaozhu.com/search-duanzufang-p{}-0/'.format(number) for number in range(1, 14)]
    # 构造多页URL
    # print(urls)

    for single_url in urls:
        get_links(single_url)
        time.sleep(1)

代码分析:
(1)第1~3行导入程序需要的库,Requests库用于请求网页获取网页数据。BeautifulSoup用于解析网页数据。time库的sleep方法可以让程序暂停。

(2)第5~8行通过Chrome浏览器的开发者工具,复制User-Agent,用于伪装为浏览器,便于爬虫的稳定性。

(3)第16~22行定义了get_links()函数,用于获取进入详细页的链接。
传入URL后,进行请求和解析。通过Chrome浏览器的“检查”并“Copy selector”,可以找到进入详细页的URL链接,但URL链接并不是嵌套在标签中,而是在标签的属性信息中,如下图:

URL链接位置

前面说到可用get_text()方法获取标签中的文本信息,但标签中的属性信息需要通过get("attr")方法获取得到,URL链接在href中,这时用get("href")便可得到网页的URL.

最后调用get_info()函数,传入的参数为获取到的网页详细页的链接。

(4)第24~42行定义get_info()函数,用于获取网页信息并输出信息。
传入URL后,进行请求和解析。通过Chrome浏览器的“检查”并“Copy selector”,获取相应的信息,由于信息数据为列表数据结构,可以通过多重循环,构造出字典数据结构,输出并打印出来。

注意:字典中的sex调用了judgment_sex()函数。

(5)第10~14行定义judgment_sex()函数,用于判断房东的性别。

def judgment_sex(class_name):
    # 定义判断用户性别的函数
    if class_name == 'member_ico1':
        return '女'
    else:
        return '男'

如下图所示,可以看出房东的性别区分

在图中所示位区域通过Chrome浏览器的“检查”可以发现,女房东的信息为<div class="member_ico1"></div>,男房东的信息为<div class="member_ico"></div>,这时就可以通过class属性来判断房东的性别。

(6)第44~49行为程序的主入口,通过对网页URL的观察,利用列表的推导式构造13个URL,并依次调用get_links()函数,time.sleep(2)的意思是每循环一次,让程序暂停2s,防止请求网页频率过快而导致爬虫失败。


4.综合案例2----爬取酷狗TOP500的信息

利用Requests和BeautifulSoup第三方库,爬取酷狗网榜单中酷狗TOP500的信息。

4.1 爬虫思路分析

(1)爬取的内容为酷狗网榜单中酷狗TOP500的音乐信息,如下图:

(2)网页版酷狗不能手动翻页进行下一步的浏览,但通过观察第一页的URL:http://www.kugou.com/yy/rank/home/1-8888.html?from=rank

这里尝试把数字1换为数字2,再进行浏览,恰好返回的是第2页的信息,如下图。

进行多次尝试后,发现更换不同的数字即为不同页面,因此只需更改home/后面的数字即可。由于每页显示的为22首歌曲,所以总共需要23个URL。

(3)需要爬取的信息有排名情况、歌手、歌曲名和歌曲时间,如下图:

4.2 爬虫代码及分析

import requests
from bs4 import BeautifulSoup
import time

# 导入相应的库文件

# 加入请求头
headers = {
    'User-Agent': 'Mozilla/5.0 '
                  '(Windows NT 6.1; Win64; x64) '
                  'AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/69.0.3497.100 Safari/537.36'}


def get_info(url):
    # 定义获取信息的函数
    wb_data = requests.get(url, headers=headers)

    soup = BeautifulSoup(wb_data.text, "lxml")

    ranks = soup.select("#rankWrap > div.pc_temp_songlist > ul > li > span.pc_temp_num")
    # print(ranks[0].get_text())

    titles = soup.select('#rankWrap > div.pc_temp_songlist > ul > li > a')

    # print(titles[0].get('title').strip())

    times = soup.select('#rankWrap > div.pc_temp_songlist > ul > li > span.pc_temp_tips_r > span')

    # print(times[0].get_text().strip())

    for rank, title, time in zip(ranks, titles, times):
        data = {'rank': rank.get_text().strip(),
                'singer': title.get_text().strip().split('-')[0],
                'song': title.get_text().strip().split('-')[1],
                'time': time.get_text().strip(),
                }

        print(data)


# get_info('http://www.kugou.com/yy/rank/home/1-8888.html?from=rank')

if __name__ == '__main__':
    # 为程序的主入口
    urls = ['http://www.kugou.com/yy/rank/home/{}-8888.html?from=rank'.format(number) for number in range(1, 24)]
    # 构造多页URL
    # print(urls)

    for single_url in urls:
        get_info(single_url)
        time.sleep(1)

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

推荐阅读更多精彩内容

  • 一、做爬虫所需要的基础 要做一只爬虫,首先就得知道他会干些什么,是怎样工作的。所以得有一些关于HTML的前置知识,...
    小天真_5eeb阅读 1,500评论 0 9
  • 爬虫文章 in 简书程序员专题: like:128-Python 爬取落网音乐 like:127-【图文详解】py...
    喜欢吃栗子阅读 21,744评论 4 412
  • 什么是爬虫? 如果是没有接触过爬虫的人可能会有些许疑惑,爬虫是个什么东西呢?其实爬虫的概念很简单,在互联网时代,万...
    SylvanasSun阅读 5,901评论 1 11
  • 我不是一个高智商的人,从小到大对自己的记忆力就没有信心,也经常丢三落四的,不容易记住事情。我还是一个感性的人,很喜...
    AndyLooper阅读 422评论 0 0
  • 黑云压碧枝, 风雨飞千里。 窗外蝉鸣啼, 户里读书声。
    鲁岳T阅读 192评论 0 1