python简单爬虫(二)

  上一篇简单的实现了获取url返回的内容,在这一篇就要第返回的内容进行提取,并将结果保存到html中。

一 、 需求:

  抓取主页面:百度百科Python词条   https://baike.baidu.com/item/Python/407313

分析上面的源码格式,便于提取:

  • 关键词分析:位于class为lemmaWgt-lemmaTitle-title的dd元素的第一个h1标签内

 

  •  简介分析(位于class为lemma-summary的div的text内容)

  • 其他相关联的标签的分析(是a标签,且href以/item/开头)

 

 

二、抓取过程流程图:

 

 三、分析:

1. 网页下载器:

1.作用:

将互联网上URL对应的网页以HTML形式下载到本地

常用的本地下载器
  1、urllib2 Python官方基础模块
  2、requests 第三方包,功能更强大

 2.urllib下载网页的三种方法

 (1)URL传入urllib2.urlopen(url)方法

import urllib2
#直接请求
response = urllib2.urlopen('http://www.baidu.com')

#获取状态码,如果是200表示成功
code = response.getcode()

#读取内容
cont = response.read()

 (2)添加data、http header

将url、data、header传入urllib.Request方法
然后 URLlib.urlopen(request)

import urllib2

#创建Request对象
request = urllin2.Request(url)

#添加数据
request.add_data('a'.'1')

#添加http的header 将爬虫程序伪装成Mozilla浏览器
request.add_header('User-Agent','Mozilla/5.0')

#发送请求获取结果
response = urllib2.urlopen(request)

 (3)添加特殊情景的处理器

处理用户登录才能访问的情况,添加Cookie
或者需要代理才能访问 使用ProxyHandler
或者需要使用https请求

 

2.网页解析器

 1.作用:

从网页中提取有价值数据的工具

以HTML网页字符串为输入信息,输出有价值的数据和新的待爬取url列表

网页解析器种类
  1、正则表达式 将下载好的HTML字符串用正则表达式匹配解析,适用于简单的网页解析 字符串形式的模糊匹配
  2、html.parser python自带模块
  3、BeautifulSoup 第三方插件
  4、xml 第三方插件

 

原理是解析成DOM树:

 

 

 2.BeautifulSoup简介及使用方法:

 1.简介:

  BeautifulSoup:Python第三方库,用于从HTML或XML中提取数据

 

安装并测试beautifulsoup

方法1:-安装:pip install beautifulsoup4
    -测试:import bs4

方法2:pycharm--File--settings--Project Interpreter--添加beautifulsoup4

 

 2.语法介绍:

根据HTML网页字符串可以创建BeautifulSoup对象,创建好之后已经加载完DOM树
即可进行节点搜索:find_all、find。搜索出所有/第一个满足要求的节点(可按照节点名称、属性、文字进行搜索)
得到节点之后可以访问节点名称、属性、文字

如:
<a href="123.html" class="aaa">Python</a>
可根据:
节点名称:a
节点属性:href="123.html" class="aaa"
节点内容:Python

 

创建BeautifulSoup对象:

from bs4 import BeautifulSoup

#根据下载好的HTML网页字符串创建BeautifulSoup对象
soup = BeautifulSoup(
  html_doc, #HTML文档字符串
  'html.parser' #HTML解析器
  from_encoding='utf-8' #HTML文档编码
)

 

搜索节点:
方法:find_all(name,attrs,string)

#查找所有标签为a的节点
  soup.find_all('a')

#查找所有标签为a,链接符合/view/123.html形式的节点
  soup.find_all('a',href='/view/123.html')
  soup.find('a',href=re.compile('aaa')) #用正则表达式匹配内容

#查找所有标签为div,class为abc,文字为Python的节点
  soup.find_all('div',class_='abc',string='Python') #class是Python关键字 避免冲突

 由于class是python的关键字,所以讲class属性加了个下划线。


访问节点信息:
  得到节点:<a href="123.html" class="aaa">Python</a>

#获取查找到的节点的标签名称
  node.name
#获取查找到的节点的href属性
  node['href']
#获取查找到的节点的连接文字
  node.gettext()

 

四、代码实现:

spider.py

# 爬虫的入口调度器
from baike import url_manager, html_downloader, html_parser, html_outputer


class SpiderMain(object):
    def __init__(self):
        self.urlManager = url_manager.UrlManager()
        self.downloader = html_downloader.HtmlDownLoader()
        self.parser = html_parser.HtmpParser()
        self.outputer = html_outputer.HtmlOutpter()


    def craw(self,url):
        count = 1 #定义爬取几个页面
        self.urlManager.add_new_url(url)
        while self.urlManager.has_new_url():
            try:
                # 获取一个url
                new_url = self.urlManager.get_new_url()
                # 访问url,获取网站返回数据
                html_content = self.downloader.download(new_url)
                new_urls, new_datas = self.parser.parse(new_url, html_content)
                self.urlManager.add_new_urls(new_urls)
                self.outputer.collect_data(new_datas)
                print(count)
                if count == 5:
                    break
                count = count+1
            except Exception as e:
                print("发生错误",e)
        # 将爬取结果输出到html
        self.outputer.out_html()

if __name__=="__main__":
    url = 'https://baike.baidu.com/item/Python/407313'
    sm = SpiderMain()
    sm.craw(url)

url_manager.py

# url管理器
class UrlManager(object):
    def __init__(self):
        # 定义两个set,一个存放未爬取的url,一个爬取已经访问过的url
        self.new_urls = set()
        self.old_urls = set()

    # 添加一个url的方法
    def add_new_url(self,url):
        if url is None:
            return  None
        if url not in self.new_urls and url not in self.old_urls:
            self.new_urls.add(url)

    # 判断是否还有待爬取的url(根据new_urls的长度判断是否有待爬取的页面)
    def has_new_url(self):
        return len(self.new_urls) != 0

    # 定义获取一个新的url的方法
    def get_new_url(self):
        if len(self.new_urls)>0:
            # 从new_urls弹出一个并添加到old_urls中
            new_url = self.new_urls.pop()
            self.old_urls.add(new_url)
            return new_url

    # 批量添加url的方法
    def add_new_urls(self, new_urls):
        if new_urls is None:
            return
        for url in new_urls:
            self.add_new_url(url)

html_downloader.py

# 读取网页的类
import urllib.request


class HtmlDownLoader(object):
    def download(self, url):
        if url is None:
            return
        # 访问url
        response = urllib.request.urlopen(url)
        # 如果返回的状态码不是200代表异常
        if response.getcode() != 200:
            return
        return response.read()

html_parser.py

# 网页解析器类
import re
import urllib

from bs4 import BeautifulSoup


class HtmpParser(object):
    # 解析读取到的网页的方法
    def parse(self, new_url, html_content):
        if html_content is None:
            return
        soup = BeautifulSoup(html_content,'html.parser',from_encoding='utf-8')
        new_urls = self.get_new_urls(new_url,soup)
        new_datas = self.get_new_datas(new_url,soup)
        return new_urls, new_datas


    # 获取new_urls的方法
    def get_new_urls(self, new_url, soup):
        new_urls = set()
        # 查找网页的a标签,而且href包含/item
        links = soup.find_all('a',href=re.compile(r'/item'))
        for link in links:
            # 获取到a必去哦啊Ian的href属性
            url = link['href']
            # 合并url。使爬到的路径变为全路径,http://....的格式
            new_full_url = urllib.parse.urljoin(new_url,url)
            new_urls.add(new_full_url)
        return new_urls



    # 获取new_data的方法
    def get_new_datas(self, new_url, soup):
        new_datas = {}
        # 获取标题内容
        title_node = soup.find('dd',class_='lemmaWgt-lemmaTitle-title').find('h1')
        new_datas['title'] = title_node.get_text()

        #获取简介内容
        summary_node = soup.find('div',class_='lemma-summary')
        new_datas['summary'] = summary_node.get_text()

        new_datas['url'] = new_url

        return new_datas
html_outputer.py
class HtmlOutpter(object):
    # 构造方法
    def __init__(self):
        self.datas = []

    # 收集数据的方法
    def collect_data(self, new_datas):
        if new_datas is None:
            return
        # 如果数据不为空就讲数据添加datas集合中
        self.datas.append(new_datas)

    # 输出爬取到的数据到本地磁盘中
    def out_html(self):
        if self.datas is None:
            return
        file = open('C:\\Users\\liqiang\\Desktop\\实习\\python\\pythonCraw\\out.html', 'w', encoding='utf-8')
        file.write("<html>")
        file.write("<head>")
        file.write("<title>爬取结果</title>")
        # 设置表格显示边框
        file.write(r'''
        <style>
         table{width:100%;table-layout: fixed;word-break: break-all; word-wrap: break-word;}
         table td{border:1px solid black;width:300px}
        </style>
        ''')
        file.write("</head>")
        file.write("<body>")
        file.write("<table cellpadding='0' cellspacing='0'>")
        # 遍历datas填充到表格中
        for data in self.datas:
            file.write("<tr>")
            file.write('<td><a href='+str(data['url'])+'>'+str(data['url'])+'</a></td>')
            file.write("<td>%s</td>" % data['title'])
            file.write("<td>%s</td>" % data['summary'])
            file.write("</tr>")
        file.write("</table>")
        file.write("</body>")
        file.write("</html>")

运行spider.py的主函数:(结果会将提取到的结果保存到html中)

 总结:

  python的类类似于java,继承object

  python的返回值return和return None一样(None类似于java的null关键字)

 

posted @ 2018-04-18 21:28  QiaoZhi  阅读(7874)  评论(3编辑  收藏  举报