Python2实现简单的爬虫

  |   0 评论   |   0 浏览   |   夜雨飘零

前言

有时候我们需要一些网络数据来工作、学习,比如我们做深度学习的。当做一个分类任务时,需要大量的图像数据,这个图像数据如果要人工一个个下载的,这很明显不合理的,这是就要用到爬虫程序。使用爬虫程序帮我们下载所需要的图像。那么我们就开始学习爬虫吧。

爬虫的框架

整体框架

下图是爬虫的整体框架,其中包括调度端、URL 管理器、网页下载器、网页解析器、价值数据,它们的作用如下:
调度端: 主要是调用 URL 管理器、网页下载器、网页解析器,也设置爬虫的入口;
URL管理器: 管理要爬网页的 URL,添加新的 URL,标记已爬过的 URL,获取要爬的 URL;
网页下载器: 通过 URL 下载网页数据,并以字符串保存;
网页解析器: 解析网页下载器获取到的字符串数据,获取用户需要的数据;
价值数据: 所有有用的数据都存储在这里。
这里写图片描述

*图像来自慕课网课程

下图是爬虫的一个顺序图,从顺序图中可以看出调度器通过训练调用 URL 管理器、网页下载器、网页解析器来不断获取网络数据。
这里写图片描述

*图像来自慕课网课程

URL 管理器

如图所示,URL 管理器是负责管理要爬取网页的 URL 的。当有新的 URL,就把新的 URL 添加到管理器中,在添加之前还有判断 URL 是否已经存在。在获取时,先判断是否还有 URL,如果有就提前 URL 并将它移动到已爬取的列表中。这样保证不添加新的重复的 URL
这里写图片描述

*图像来自慕课网课程

网页下载器

从 URL 管理器中获取的 URL,我们要把这些 URL 的网页数据下载下来,这是就要使用到了网页下载器,这说到下载的有本地文件或字符串,这是因为当我们爬取的是文件时,如图片,下载的就是文件了。当我们爬取的是网页中的内容数据时,这时就是字符串。

这里写图片描述

*图像来自慕课网课程

网页下载器的代码片段:

# coding=utf-8
import urllib2

url = "https://www.baidu.com"
response = urllib2.urlopen(url)
code = response.getcode()
content = response.read()

print "状态码:", code
print "网页内容", content

还可以添加请求头,模仿其他浏览器访问

# coding=utf-8
import urllib2

url = "https://www.baidu.com"
request = urllib2.Request(url)
# 模仿火狐浏览器
request.add_header("user-agent", "Mozilla/5.0")
response = urllib2.urlopen(request)
code = response.getcode()
content = response.read()

print "状态码:", code
print "网页内容", content

输出信息为:

状态码: 200
网页内容 <html>
<head>
	<script>
		location.replace(location.href.replace("https://","http://"));
	</script>
</head>
<body>
	<noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
</body>
</html>

网页解析器

在网页下载器中下载的众多字符串中,我们要提前我们需要的数据,如新的要爬取的 URL、我们需要的网页数据。通过这个网页解析器就可以解析这些数据了。获取新的 URL 可以添加到 URL 管理器中,获取有用的数据就将它保存。
这里写图片描述

*图像来自慕课网课程

网页解析器的代码片段:

# coding=utf-8
from bs4 import BeautifulSoup

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
"""

soup = BeautifulSoup(html_doc, 'html.parser', from_encoding='utf-8')
# 寻找属性class为title的p标签
title_all = soup.find('p', class_="title")
print title_all
# 获取该标签对应的内容
title = title_all.get_text()
print title

输出信息如下:

<p class="title"><b>The Dormouse's story</b></p>
The Dormouse's story

爬虫程序

这个程序是爬取 CSDN 博客的文章,并爬取相关的文章。比如我们的爬虫入口是一篇《把项目上传到码云》的文章,在每章文章的最后都有相关的文章推荐,这些推荐的文章的 URL 就是我们补充的 URL 来源。如:
这里写图片描述

然后观察整个文章的网页源码,可以得到文章的标题的代码片段如下,关键定位信息是 class="csdn_top"

<article>
    <h1 class="csdn_top">把项目上传到码云</h1>
    <div class="article_bar clearfix">
        <div class="artical_tag">
            <span class="original">
            原创                </span>
            <span class="time">2017年04月15日 20:39:02</span>
        </div>

文章内容的代码片段如下,关键定位信息是 class="article_content csdn-tracking-statistics tracking-click"

 <div id="article_content" class="article_content csdn-tracking-statistics tracking-click" data-mod="popu_519" data-dsm="post">
                            <div class="markdown_views">
                        <p>一、为什么要使用码云而不使用GitHub?会有很多朋友这样问,原因有以下几条: <br>

推荐文章的代码片段如下,关键定位信息是 strategy="BlogCommendFromBaidu_0"

      <div class="recommend_list clearfix" id="rasss">
                                          <dl class="clearfix csdn-tracking-statistics recommend_article" data-mod="popu_387" data-poputype="feed"  data-feed-show="false"  data-dsm="post">
                <a href="https://blog.csdn.net/Mastery_Nihility/article/details/53020481"  target="_blank" strategy="BlogCommendFromBaidu_0">
                    <dd>
                        <h2>上传项目到开源中国码云</h2>
                        <div class="summary">
                            上传项目到开源中国码云 
                        </div>

有了这些定位,就可以开始爬取数据了,我们开始吧。

调度器

创建一个 spider_mamin.py 文件来编写调度器的代码,这个就是调度中心,在这里控制整个爬虫程序:

# coding=utf-8
import html_downloader
import html_outputer
import html_parser
import url_manager

class SpiderMain(object):
    # 调度程序
    def __init__(self):
        # 获取URL管理器
        self.urls = url_manager.UrlManager()
        # 获取网页下载器
        self.downloader = html_downloader.HtmlDownloader()
        # 获取网页解析器
        self.parser = html_parser.HtmlParser()
        # 获取数据输出器
        self.output = html_outputer.HtmlOutput()

    def craw(self, root_url, max_count):
        count = 1
        # 添加爬虫入口的跟路径
        self.urls.add_new_url(root_url)
        # 创建一个循环,如果URL管理器中还有新的URL就一直循环
        while self.urls.has_new_url():
            try:
                # 从URL管理器中获取新的URL
                new_url = self.urls.get_new_url()
                print 'craw %d : %s ' % (count, new_url)
                # 下载网页
                html_cont = self.downloader.downloader(new_url)
                # 解析网页数据
                new_urls, new_data = self.parser.parser(new_url, html_cont)
                # 添加新的URL
                self.urls.add_new_urls(new_urls)
                # 添加新的数据
                self.output.collect_data(new_data)
                # 满足爬取数量及中断
                if count == max_count:
                    break
                count = count + 1
            except Exception, e:
                print '爬取失败:', e
        # 输出数据
        self.output.output_html()

if __name__ == '__main__':
    # 爬虫的根URL
    root_url = "https://blog.csdn.net/qq_33200967/article/details/70186759"
    # 爬取的数量
    max_count = 100
    obj_spider = SpiderMain()
    # 启动调度器
    obj_spider.craw(root_url, max_count)

URL 管理器

创建一个 url_manager.py 文件编写 URL 管理器的代码,添加新的 URL 和提供 URL 给网页下载器,由这个程序负责:

# coding=utf-8

class UrlManager(object):
    # url管理器
    def __init__(self):
        self.new_urls = set()
        self.old_urls = set()

    # 向管理器中添加一个新的url
    def add_new_url(self, url):
        if url is None:
            return
        # 判断要添加的URL是否已存在新列表或者旧列表中
        if url not in self.new_urls and url not in self.old_urls:
            self.new_urls.add(url)

    # 向管理器中添加批量url
    def add_new_urls(self, urls):
        if urls is None or len(urls) == 0:
            return
        for url in urls:
            # 添加新的URL
            self.add_new_url(url)

    # 判断管理器中是否有新的待爬取的url
    def has_new_url(self):
        return len(self.new_urls) != 0

    # 从url中获取一个新的待爬取的url
    def get_new_url(self):
        # 获取并移除最先添加的URL
        new_url = self.new_urls.pop()
        # 把这个路径添加到已爬取的列表中
        self.old_urls.add(new_url)
        return new_url

网页下载器

创建一个 html_downloader.py 文件来编写网页下载器的代码,下载网页的字符串数据,都是 HTML 的代码:

# coding=utf-8
import urllib2

class HtmlDownloader(object):
    # html下载器
    def downloader(self, url):
        # 如果路径为空就返回空
        if url is None:
            return None
        # 打开网页数据
        response = urllib2.urlopen(url)
        # 判断是否访问成功,如果不成功就返回空
        if response.getcode() != 200:
            return None
        # 返回网页数据
        return response.read()

网页解析器

创建一个 html_parser.py 文件来编写网页解析器的代码,从网页下载器获取的 HTML 格式的字符串中解析想要的数据个 URL:

# coding=utf-8
import re
from bs4 import BeautifulSoup

class HtmlParser(object):
    def parser(self, page_url, html_cont):
        """
        # html解析器
        :param page_url: 网页的URL
        :param html_cont: 网页的字符串数据
        :return: 网页包含相关的URL和文章的内容
        """
        # 判断网页URL和网页内容是否为空
        if page_url is None or html_cont is None:
            return
        # 获取解析器
        soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')
        # 获取解析到的URL
        new_urls = self._get_new_urls(soup)
        # 获取解析到的文章数据
        new_data = self._get_new_data(page_url, soup)
        return new_urls, new_data

    # 解析相关文章的URL
    def _get_new_urls(self, soup):
        new_urls = set()
        # 获取相关的文章URL,格式如下:
        # <a href="https://blog.csdn.net/qq_18601953/article/details/78395878"
        # target="_blank" strategy="BlogCommendFromBaidu_7">
        links = soup.find_all('a', strategy=re.compile(r"BlogCommendFromBaidu_\d+"))
        # 提取所有相关的URL
        for link in links:
            new_url = link['href']
            new_urls.add(new_url)
        return new_urls

    # 解析数据
    def _get_new_data(self, page_url, soup):
        res_data = {}
        # 获取URLurl
        res_data['url'] = page_url

        # 获取标题<h1 class="csdn_top">把项目上传到码云</h1>
        essay_title = soup.find('h1', class_="csdn_top")
        res_data['title'] = essay_title.get_text()

        # 内容标签的格式如下:
        # <div id="article_content" class="article_content csdn-tracking-statistics tracking-click"
        # data-mod="popu_519" data-dsm="post">
        essay_content = soup.find('div', class_="article_content csdn-tracking-statistics tracking-click")
        res_data['content'] = essay_content.get_text()
        return res_data

数据存储器

创建一个 html_outputer.py 文件来编写存储数据的代码,当爬取完成数据之后,通过这个程序永久保存爬取的数据:

# coding=utf-8

class HtmlOutput(object):
    #html输出器
    def __init__(self):
        self.datas = []

    #收集数据
    def collect_data(self, data):
        if data is None:
            return
        self.datas.append(data)

    #将收集好的数据写出到html文件中
    def output_html(self):
        fout = open('output.html','w')

        fout.write("<html>")
        fout.write("<body>")
        fout.write("<table>")
        if len(self.datas) == 0:
            print "数据为空!"
        #ascii
        for data in self.datas:
            fout.write("<tr>")
            fout.write("<td>%s</td>" % data['url'])
            fout.write("<td>%s</td>" % data['title'].encode('utf-8'))
            fout.write("<td>%s</td>" % data['content'].encode('utf-8'))
            fout.write("</tr>")

        fout.write("</table>")
        fout.write("</body>")
        fout.write("</html>")

        fout.close()

运行代码

运行调度器代码 spider_mamin.py,可以看到爬取过程输出的日志信息,如果出现失败是正常的:

craw 1 : https://blog.csdn.net/qq_33200967/article/details/70186759 
craw 2 : https://blog.csdn.net/qq_18601953/article/details/78395878 
craw 3 : https://blog.csdn.net/wust_lh/article/details/68068176 

爬取完成之后,所有的数据都会以 HTML 格式存储在 output.html 中。可以在浏览器中打开,如:
这里写图片描述
为了读者方便使用代码,我已将这些代码打包了,可以在这里下载完整代码。

参考资料

  1. http://www.imooc.com/learn/563

标题:Python2实现简单的爬虫
作者:夜雨飘零
地址:https://blog.doiduoyi.com/articles/1584971916912.html

评论

发表评论