Scrapy 分布式爬虫
elasticsearch
django
环境配置和基础知识铺垫
技术选型
scrapy
requests + beautifusoup
都是库
scrapy 是框架
scrapy 框架中可以加入 requests 和 beautifulsoup
scrapy 基于 twieted (异步IO框架) 性能是最大的优势
scrapy 方便扩展 提供了内置的很多功能
scrapy 内置的 css 和 xpath selector 非常方便 # bs最大的缺点是慢
常见类型的服务
静态网页 可以用 github 来代理静态博客
动态网页
webservice(restapi)
爬虫作用
搜索引擎 垂直领域搜索引擎 baidu google
推荐引擎 今日头条
机器学习的数据样本
数据分析 舆情分析
爬虫基础知识
正则表达式
特殊字符 提取字符
^ $ * ? + {2} {2, } {2, 5} |
[] [^] [a-z] .
\s \S \w \W
[\u4E00-\u9FA5] () \d
深度优先和广度优先遍历算法
网站的树结构
url设计
分层设计
www.jobbole.com
top.jobbole.com
blog.jobbole.com
web.jobbole.com
python.jobbole.com
android.jobbole.com
ios.jobbole.com
hao.jobbole.com
group.jobbole.com
date.jobbole.com
design.jobbole.com
fanyi.jobbole.com
java专栏 www.importnew.com/
深度优先算法和实现
递归实现
广度优先算法和实现
url 的去重策略
将访问过的url保存到数据库中
将访问过的url保存到内存中 保存到set中 当url比较多的情况 内存占用比较大
url经过md5等方法哈希后保存到set中 128bit
用bitmap方法 将访问过的url通过hash函数映射到某一位
bloomfilter方法对bitmap进行改进 多重hash函数降低冲突
unicode 和 utf-8 编码
8 bit = 1 byte
ASCII 1byte
GB2312 2byte (3byte or 4byte)
unicode 16bit or 32bit
utf8
英文 1byte
汉字 3byte
生僻字 4-6byte
爬取真实数据
通过 scrapy 爬取
知名技术社区
blog.jobbole.com
手动创建 scrapy项目
main.py 启动/调试爬虫
spider/jobbole.py 网页解析
xpath实现
css选择器实现
items.py 解析内容对象
pipelines.py 数据下载和存储
自定义json文件的导出
调用scrapy提供的json exporter导出json文件
保存到mysql数据库
采用异步的方式入库mysql
还可以用scrapy-djangoitem
使用ItemLoader + 输入字段预处理的Item
知名问答网站
知名招聘网站
Scrapy 的不同功能
网站结构和网络请求
xpath + css 选择器
模拟登陆
scrapy
spider
item
item loader
pipeline
feed export
CrawlSpider
scrapy 突破反爬虫技术
图片验证码
IP 访问频率限制
user-agent 随机切换
scrapy 进阶
scrapy 的原理
基于 scrapy 的中间件开发
动态网站的抓取处理
将 selenium 和 phantomjs 集成到 scrapy 中
scrapy log 配置
email 发送
scrapy 信号
scrapy-redies 分布式爬虫
多台服务器 分布式爬虫
理解 scrapy-redies 分布式爬虫
使用
源码分析
集成 bloomfilter 到 scrapy-redies 中
elasticsearch django 实现搜索引擎
搭建搜索引擎网站
搜索引擎原理
开发爬虫所需要用到的技术 以及 网站分析技巧
理解 scrapy 的原理 和所有组件的使用以及分布式爬虫 scrapy-redies 的使用和原理
理解分布式开源搜索引擎 elasticsearch 的使用和搜索引擎的原理
体验 django 快速搭建网站
# 二叉树的深度优先遍历算法
def depth_tree(tree_node):
if tree_node is not None:
print(tree_node._data)
if tree_node._left is not None:
return depth_tree(tree_node._left)
if tree_node._right is not None:
return depth_tree(tree_node._right)
# 二叉树的广度优先遍历算法
def level_tree(root):
if root is not None:
return
my_quere = []
node = root
my_quere.append(node)
while my_quere:
node = my_quere.pop(0)
print(node.elem)
if node.lchild is not None:
my_quere.append(node.lchild)
if node.rchild is not None:
my_quere.append(node.rchild)
手动创建 scrapy项目
fulai@fwwb9:~/PycharmProjects$ scrapy startproject article_spider
New Scrapy project 'article_spider', using template directory '/opt/anaconda3/lib/python3.6/site-packages/scrapy/templates/project', created in:
/home/fulai/PycharmProjects/article_spider
You can start your first spider with:
cd article_spider
scrapy genspider example example.com
scrapy 项目目录结构
fulai@fwwb9:~/PycharmProjects/article_spider$ tree
.
├── article_spider
│ ├── __init__.py
│ ├── items.py 定义数据保存的格式
│ ├── middlewares.py 可以存放自定义的middleware
│ ├── pipelines.py 与数据存储相关
│ ├── __pycache__
│ ├── settings.py scrapy的配置
│ └── spiders 存放具体的某个网站的爬虫
│ ├── __init__.py
│ └── __pycache__
└── scrapy.cfg 配置文件 类似于django
4 directories, 7 files
创建某个网站的爬虫文件模板
fulai@fwwb9:~$ cd PycharmProjects/article_spider/
fulai@fwwb9:~/PycharmProjects/article_spider$ scrapy genspider jobbole blog.jobbole.com
Created spider 'jobbole' using template 'basic'
启动 scrapy
fulai@fwwb9:~/PycharmProjects/article_spider$ scrapy crawl jobbole
xpath
xpath简介
使用路径表达式在xml和html中进行导航
包含标准函数库
是一个w3c的标准
xpath术语
父节点
子节点
同胞节点
先辈节点
后代节点
xpath语法
article 选取所有article元素的所有子节点
/article 选取根元素article
article/a 选取所有属于article的子元素的a元素
//div 选取所有div元素 不论出现在文档的任何地方
article//div 选取所有属于article的后代的div元素
//@class 选取所有名为class的属性
谓语
/article/div[1]
/article/div[last()]
/article/div[last()-1]
//div[@lang] 选取所有拥有lang属性的div元素
//div[@lang='eng'] 选取所有lang属性为eng的div元素
特殊符号
/div/× 选取属于div元素的所有子节点
//× 选取所有元素
//div[@*] 选取所有带属性的div元素
/div/a|/div/p 选取所欲div元素的a和p元素
//span|//ul 选取文档中的span元素和ul元素
article/div/p|//span
shell调试
好处:只进行一次http请求
scrapy shell http://blog.jobbole.com/113735/
css 选择器
× 所有节点
#container id
.container 类
li a 后代元素
ul + p 选择ul元素后面的第一个p元素
div#container > ul 第一个子元素
ul ~ a 相邻的所有的a元素
a[title]
a[href='http://jobbole.com']
a[href*='jobbole'] 包含
a[href^=‘http’] 开头
a[href$='.jpg'] 结尾
input[type=radio]:checked
div:not(#container)
li:nth-child(3) 选取第三个li元素
tr:nth-child(2n) 第偶数个tr
爬取文章内容的xpath实现和css选择器实现
# -*- coding: utf-8 -*-
import re
import scrapy
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']
start_urls = ['http://blog.jobbole.com/113674/']
def parse(self, response):
# # firefox
# re_selector = response.xpath('/html/body/div[1]/div[3]/div[1]/div[1]')
# print(re_selector)
# # chrome
# re2_selector = response.xpath('//*[@id="post-113735"]/div[1]/h1/text()')
# print(re2_selector)
# re3_selector = response.xpath('//*[@class="entry-header"]/h1/text()')
# print(re3_selector.extract()[0])
# 标题
# xpath实现
title = response.xpath('//*[@id="post-113735"]/div[1]/h1/text()')
# css选择器实现
title = response.css('.entry-header h1::text')
# 创建时间
# xpath
create_date = response.xpath('//p[@class="entry-meta-hide-on-mobile"]/text()')
# css
create_date = response.css('p.entry-meta-hide-on-mobile::text')
create_date = create_date.extract()[0].strip().replace("·", '').strip()
# 点赞数
# xpath
praise_nums = int(response.xpath('//span[contains(@class, "vote-post-up")]/h10/text()')
.extract()[0])
# css
praise_nums = int(response.css('.vote-post-up h10::text').extract()[0])
# 收藏数
fav_nums = 0
# xpath
match_res = re.match(r'.*?(\d+).*', response.xpath('//span[contains(@class, "bookmark-btn")]/text()')
.extract()[0])
# css
match_res = re.match(r'.*?(\d+).*', response.css('span.bookmark-btn::text')
.extract()[0])
if match_res:
fav_nums = match_res.group(1)
# 评论数
comment_nums = 0
# xpath
match_res = re.match(r'.*?(\d+).*', response.xpath('//a[@href="#article-comment"]/span')
.extract()[0])
# css
match_res = re.match(r'.*?(\d+).*', response.css('a[href="#article-comment"]::text')
.extract()[0])
if match_res:
comment_nums = match_res.group(1)
# 标签
# xpath
tag_list = response.xpath('//p[@class="entry-meta-hide-on-mobile"]/a/text()').extract()
# css
tag_list = response.css('p[class="entry-meta-hide-on-mobile"] a::text').extract()
tag_list = [element for element in tag_list if not element.strip().endswith('评论')]
tag = ','.join(tag_list)
# 文章内容
# xpath
content = response.xpath('//div[@class="entry"]').extract()[0]
# css
content = response.css('div[class="entry"]::text').extract()[0]
pass
两个函数
extract()[x]
extract_first()
session 和 cookies的区别
http状态码
200 请求被成功处理
301/302 永久性重定向/临时性重定向
403 没有权限访问
404 资源未找到
500 服务器内部错误
503 服务器停机或正在维护