Python3 简单爬虫框架

目录

  • 爬虫简介
  • 调度器
  • URL管理器
  • 下载器
  • 解析器
  • 输出器
  • 实例 Demo

简介

爬虫是一段自动抓取互联网信息的程序,将互联网数据为我所用!(图片来源于慕课网)

爬虫架构
爬虫架构流程

调度器

负责URL管理器、下载器、解析器、输出器模块之间工作的协调,也是爬虫程序的入口

URL管理器

管理待抓取的URL集合和已抓取的URL集合,防止重复抓取和循环抓取

URL管理器
URL管理器的几种实现方式

网页下载器

将互联网上URL对应的网页下载到本地的工具

网页下载器流程
二种网页下载器

urllib

python2.x里urllib2库,在python3.x里,urllib2改名为urllib,被分成一些子模块:urllib.request、urllib.parse和urllib.error,这里我用的python3.x;

第一种方式:

发送一个GET请求到指定的页面,然后返回HTTP的响应:

from urllib import request

url = 'http://www.baidu.com'
# 直接请求
response = request.urlopen(url)
# 获取状态码,如果是200表示成功
print(response.status)
# 读取网页内容
print(response.read().decode('utf-8'))

第二种方式:

模拟浏览器发送GET请求,就需要使用Request对象,通过往Request对象添加HTTP头,我们就可以把请求伪装成浏览器进行访问:

  • User-Agent 有些 Server 或 Proxy 会检查该值,用来判断是否是浏览器发起的 Request
  • .Content-Type 在使用 REST 接口时,Server 会检查该值,用来确定 HTTP Body 中的内容该怎样解析。
第二种方法
from urllib import request
req = request.Request('http://www.baidu.com')
req.add_header('User-Agent', 'Mozilla/6.0')
response =  request.urlopen(req)
# 获取状态码,如果是200表示成功
print(response.status)
# 读取网页内容
print(response.read().decode('utf-8'))

第三种方式:

模拟浏览器发送POST请求,有些网页需要用户登录才能访问,需要使用cookie处理,则使用HTTPCookieProcessor,有些网页使用代理访问的,则使用ProxyHandler,有些网页是Https加密访问的,则需要使用HTTPSHandler,有些网页URL相互跳转访问,则需要使用HTTPRedirectHandler;

第三种方式
HTTPCookieProcessor

cookie中保存中我们常见的登录信息,有时候爬取网站需要携带cookie信息访问,这里用到了http.cookijar,用于获取cookie以及存储cookie,同时cookie可以写入到文件中保存,一般有两种方式http.cookiejar.MozillaCookieJar和http.cookiejar.LWPCookieJar()

保存cookie到文件
#保存cookie的文件
import urllib.request
import http.cookiejar

filename = 'cookie.txt'
# 声明一个MozillaCookieJar对象实例(cookie)来保存cookie,后面写入文件
url = 'http://www.zhihu.com'
cookie = http.cookiejar.MozillaCookieJar(filename)
# 还是创建处理器
handler = urllib.request.HTTPCookieProcessor(cookie)
# 创建支持处理HTTP请求的opener对象
opener = urllib.request.build_opener(handler)
opener.open(url)
# 保存cookie到文件
cookie.save(ignore_discard=True, ignore_expires=True)
# ignore_discard表示即使cookie将被丢弃也将保存下来,ignore_expires表示如果该文件中cookie已经存在,则覆盖原文件写入
从文件取出cookie
import urllib.request
import http.cookiejar

url = 'http://www.zhihu.com'
# 声明CookieJar对象实例来保存cookie
cookie = http.cookiejar.MozillaCookieJar()
# 从文件中读取内容到cookie变量中
cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
# 处理器
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
print(opener.open(url).read())
cookie存储内存
import urllib.request
import http.cookiejar

# 自动记住cookie
url = 'http://www.zhihu.com'
# 声明cookiejar的对象,存放cookie的容器
cookie = http.cookiejar.CookieJar()  
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open(url)
# 获取状态码,如果是200表示成功
print(response.status)
# 读取网页内容
print(response.read().decode('utf-8'))

print("\n")
for item in cookie:
    print(item.name + '=' + item.value)

注意: python3用cookielib 模块改名为 http.cookiejar,带cookie的打印出来必须用opener.open(req).read().decode('utf-8')来发送的请求才会带上cookie,如果用urllib.request.urlopen()是不带cookie的

ProxyHandler代理

网站它会检测某一段时间某个IP 的访问次数,如果访问次数过多,它会禁止你的访问,所以这个时候需要通过设置代理来爬取数据

import urllib.request

url = 'http://www.baidu.com'
proxy_handler = urllib.request.ProxyHandler({
    #代理服务器IP地址
    'http': 'http://111.13.100.91:80',
    'https': 'https://111.13.100.91:443'
})
opener = urllib.request.build_opener(proxy_handler)
response = opener.open(url)
# 获取状态码,如果是200表示成功
print(response.status)
# 读取网页内容
print(response.read().decode('utf-8'))

网页解析器

几种网页解析器
解析器 使用方法 优势 劣势
Python标准库 BeautifulSoup(markup, "html.parser") 1、Python的内置标准库
2、执行速度适中
3、文档容错能力强
Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器 BeautifulSoup(markup, "lxml") 1、速度快
2、文档容错能力强
需要安装C语言库
lxml XML 解析器 BeautifulSoup(markup, ["lxml-xml"])
BeautifulSoup(markup, "xml")
1、速度快
2、唯一支持XML的解析器
需要安装C语言库
html5lib BeautifulSoup(markup, "html5lib") 1、最好的容错性
2、以浏览器的方式解析文档
3、生成HTML5格式的文档
1、速度慢
2、不依赖外部扩展
解析分类
Beautiful Soup4解析

Beautiful Soup4 是一个可以从HTML或XML文件中提取数据的Python库,它能够通过你喜欢的转换器实现惯用的文档导航、查找、修改文档的方式,Beautiful Soup会帮你节省数小时甚至数天的工作时间, Beautiful Soup4 中文官网

Document结构
解析过程

Beautiful Soup4 属性

bs4主要使用find()方法和find_all()方法来搜索文档。
find()用来搜索单一数据,find_all()用来搜索多个数据

# 根据HTML网页字符串创建BeautifulSoup对象
soup = BeautifulSoup(
    html_doc, #HTML文档字符串
    'html.parser', #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_all('a', href=re.comile(r'/view/\d+\.htm'))

# 查找所有标签为div,class为abc,文字为Python的节点
soup.find_all('div',class_='abc', string='Python')

# 得到节点:Python

# 获取查找到节点的标签名称
node.name

# 获取查找到的a节点的href属性
node.get('href')

# 获取查找到的a节点的链接文字
node.get_text()
解析本地HTML
from bs4 import BeautifulSoup

html_doc = '''


    
        
        测试页面
    
    
        baidu
        zhifu
        douban
    

'''

# 根据HTML网页字符串创建BeautifulSoup对象
soup = BeautifulSoup(
    html_doc,  # HTML文档字符串
    'html.parser',  # HTML解析器
)

# 第一种方式-查找所有a标签
print('第一种方式')
links = soup.find_all('a')
for link in links:
    print(link.name, link.get('href'), link.get_text())

# 第二种方式-指定url查找
print('第二种方式')
link_node = soup.find('a', href = 'http://www.zhihu.com')
print(link_node.name, link_node.get('href'), link_node.get_text())

# 第三种方式-模糊查找
print('第三种方式')
link_node = soup.find('a', href=re.compile('ban'))
print(link_node.name, link_node.get('href'), link_node.get_text())

# 第四种方式-通过class查找
print('第四种方式')
link_node = soup.find('a', class_='baidu_link')
print(link_node.name, link_node.get('href'), link_node.get_text())
运行结果

输出器

主要是输出我们需要存储的数据,数据存储可以以不同形式(.txt、.doc、.db、html、.jpg等)输出,爬取的内容中含有中文,一定要指定encoding为utf-8,不然字符会出现乱码现象

实例分析

爬取百度图片

百度图片
URL分析

http://image.baidu.com/search/index?tn=baiduimage&ps=1&ct=201326592&lm=-1&cl=2&nc=1&ie=utf-8&word=%E5%9B%BE%E7%89%87

其中关键字进行编码

import urllib
from urllib import parse

data = {'word': '图片'}
encode_data = urllib.parse.urlencode(data)
print(encode_data)

运行结果:

word=%E5%9B%BE%E7%89%87

网页爬虫的步骤
spider_main 调度器
# coding:utf8
# 整个框架调度器
import url_manager, html_downloader, html_parser, pic_outputer

import urllib
from urllib import parse


class SpiderMain(object):
    def __init__(self):
        self.urls = url_manager.UrlManager()  # url管理器
        self.downloader = html_downloader.HtmlDownloader()  # html下载器
        self.parser = html_parser.HtmlParser()  # html解析器
        self.outputer = pic_outputer.PicOutputer()  # 文件输出

    def spider(self, keyword):
        print('正在查找 "' + keyword + '" 对应的图片,下载中,请稍后...')
        # 主链接url
        base_url = "http://image.baidu.com/search/index?tn=baiduimage&ps=1&ct=201326592&lm=-1&cl=2&nc=1&ie=utf-8"

        #  拼接字典
        data = {"word": keyword}
        encode_data = urllib.parse.urlencode(data)
        spider_url = base_url + "&" + encode_data
        print("搜索图片的链接: ", spider_url)

        # 下载html源码
        result = self.downloader.download(spider_url)

        # print("网页结果:", result)
        # 解析下载页面中图片链接
        new_urls = self.parser.parse(result)

        # 将图片链接集放到url管理器中
        self.urls.add_new_urls(new_urls)

        # 遍历下载url文件
        while self.urls.has_new_url():
            try:
                self.outputer.out_picture(self.urls.get_new_url(), keyword)
                print("总爬取数量:", self.urls.get_urls_size(), "已爬取数量:", self.urls.get_oldurl_size())
                if self.urls.get_urls_size() == self.urls.get_oldurl_size():
                    print("图片下载完成")
            except Exception as e:
                print('spider failed--', e)


if __name__ == "__main__":
    obj_spider = SpiderMain()
    # 获取关键字
    keyword = input("请输入你要baidu的图片关键字:")
    obj_spider.spider(keyword)
html_downloader 网页下载器
import urllib
import logging
from urllib import request


# 网页下载器
class HtmlDownloader(object):

    def __init__(self):
        self.TAG = "HtmlDownloader"

    def download(self, url):
        if url is None:
            logging.warning(self.TAG, "url不合法: ", url)
            return None

        try:
            # GET方法
            req = urllib.request.Request(url)
            #  设置超时10秒
            response = request.urlopen(req, timeout=10)
            if response.getcode() != 200:
                logging.warning(self.TAG, "错误状态码: ", response.getcode())
                return None
            return response.read().decode('utf-8')
        except ConnectionError:
            logging.error(self.TAG, "您当前请求的URL地址出现错误: ", url)
            return None
html_parser网页解析器
import re


#  网页解析器
class HtmlParser(object):

    def __init__(self):
        self.urls = set()

    # 获取集合
    def _get_new_urls(self):
        return self.urls

    # 通过网页源码爬取url
    def parse(self, html_cont):
        for addr in re.findall('"objURL":"(.*?)"', html_cont, re.S):  # 查找URL
            print('正在爬取URL地址:' + str(addr))  # 打印爬取的地址
            self.urls.add(addr)
        return self.urls
url_manager管理器
#  url管理器
class UrlManager(object):
    def __init__(self):
        # 存放新url
        self.new_urls = set()
        # 存放爬取之后url
        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 has_new_url(self):
        return len(self.new_urls) != 0

    # 获取新的url
    def add_new_urls(self, urls):
        if urls is None or len(urls) == 0:
            return
        for url in urls:
            self.add_new_url(url)

    # 添加批量urls
    def get_new_url(self):
        new_url = self.new_urls.pop()
        self.old_urls.add(new_url)
        return new_url

    # 获取待爬取数量
    def get_newurl_size(self):
        return len(self.new_urls)

    # 获取已经爬取数量
    def get_oldurl_size(self):
        return len(self.old_urls)

    # 获取总的数量
    def get_urls_size(self):
        return len(self.new_urls) + len(self.old_urls)
pic_outputer图片输出器
# 将获取到的图片下载本地
import urllib.request
import logging


class PicOutputer(object):

    # 初始化集合
    def __init__(self):
        self.TAG = "PicOutputer"
        # 计数单位
        self.count = 0

    # 收藏需要下载url链接
    def out_picture(self, url, keyword):
        if url is None and url.startswith("http") or url.startswith("https"):
            logging.warning(self.TAG, "url不合法: ", url)
            return

        try:
            # 下载链接图片,也可以采用文件流形式存储
            urllib.request.urlretrieve(url,
                                       r'E:\PythonCode\spiserbaidupics\file\%s.jpg' %(keyword + "_"+str(self.count)))
            self.count += 1
        except Exception as e:
            print('save failed--', e)

整个项目结构
项目结构
下载完成的图片
下载完成的图片

我们不只可以下载“图片”,还可以下载美女图片,搜索关键字进行下载:

美女图片

你可能感兴趣的:(Python3 简单爬虫框架)