目录
- 爬虫简介
- 调度器
- URL管理器
- 下载器
- 解析器
- 输出器
- 实例 Demo
简介
爬虫是一段自动抓取互联网信息的程序,将互联网数据为我所用!(图片来源于慕课网)
调度器
负责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 中文官网
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)
整个项目结构
下载完成的图片
我们不只可以下载“图片”,还可以下载美女图片,搜索关键字进行下载: