伯乐在线爬虫项目目的及项目准备:
1.使用scrapy创建项目
2.创建爬虫,bole 域名 jobbole.com
3.Start_urls = [‘http://blog.jobbole.com/all-posts/’]
4.爬取所有页数的文章
5.文章列表页需要数据
a) 缩略图的地址
b) 详情url地址
6.详情页面要提取的数据
# 博客标题
# 博客创建时间
# 博客url
# 将url经过md5加密生成id
# 缩略图的地址
# 图片保存路径 #这个属性考虑好在哪赋值
# 点赞数
# 收藏数
# 评论数
# 博客作者
# 博客标签
7.将图片下载,保存到imgs文件夹中
8.将爬取的所有数据存储到数据库
创建项目我们在cmd中进行创建,在开始之前我们要将数据库表以及其中字段创建好。
spider爬虫代码:
# -*- coding: utf-8 -*-
import scrapy
import re
# ..上级目录
from ..items import BoleItem
# .同级目录
from .tools import get_number,md5
# MVC Model view controler
# 降低代码耦合性
# MVVC
# 做url地址的拼接,如果没有域名才会进行拼接
import urlparse
class BoleSpider(scrapy.Spider):
name = 'bole'
allowed_domains = ['jobbole.com']
start_urls = ['http://blog.jobbole.com/all-posts/']
def parse(self, response):
# 获取每一页的文章url和图片url
a_list = response.xpath(".//*[@id='archive']/div/div[1]/a")
for a in a_list:
# 获取博客详情页面url
# 获取博客图片的url
a_href = a.xpath("@href").extract_first('')
img_src = a.xpath("img/@src").extract_first('')
yield scrapy.Request(
url=a_href,
callback=self.parse_detail,
meta={"img_src":img_src}
)
next_page = response.xpath("//a[@class='next page-numbers']/@href").extract_first('')
if next_page:
# 发送请求,请求下一页
yield scrapy.Request(
url=next_page
)
def parse_detail(self,response):
# 博客详情地址
blog_url = response.url
# 图片url地址
img_src = response.meta["img_src"]
# 图片地址有可能不完整
if img_src:
# 1.拼接URL地址
img_src = urlparse.urljoin('http://www.jobbole.com',img_src)
else:
img_src = ''
# 博客标题
title = response.xpath("//div[@class='entry-header']/h1/text()").extract_first('')
# 博客发布时间
blog_date = response.xpath("//p[@class='entry-meta-hide-on-mobile']/text()").extract_first('').strip().replace(u'·','').strip()
# 所有的标签
tags = response.xpath("//p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
# join 将列表中所有的字符串拼接,并以,隔开
# split 将字符串根据某个字符进行分割,返回一个列表
tags = ','.join(tags)
# 点赞数
like_count = response.xpath("//h10/text()").extract_first('')
# 没有点赞设置为0
if like_count:
like_count = int(like_count)
else:
like_count = 0
# 评论数
comment_count = response.xpath("//a[@href='#article-comment']/span/text()").extract_first('')
comment_count = get_number(comment_count)
# 收藏数
bookmark_count = response.xpath("//span[contains(@class,'bookmark-btn')]/text()").extract_first('')
bookmark_count = get_number(bookmark_count)
# blog_id
# img_path
# 创建Item对象
item = BoleItem()
item['blog_id'] = md5(response.url)
item["title"] = title
item["blog_url"] = blog_url
# 将图片url放在列表中
item["img_src"] = [img_src]
item["blog_date"] = blog_date
item["tags"] = tags
item["like_count"] = like_count
item["comment_count"] = comment_count
item["bookmark_count"] = bookmark_count
print title,blog_date,blog_url,img_src,like_count,comment_count,bookmark_count,tags
yield item
创建工具包tools,优化代码,工具包tools代码:
import hashlib
# md5加密函数
def md5(str):
import hashlib
m = hashlib.md5()
m.update(str)
return m.hexdigest()
# 只要以后需要从字符串中匹配数字,就可以使用这个函数
import re
def get_number(str):
# 正则
pattern = re.compile(r'\d+')
rs = re.search(pattern, str)
if rs:
count = int(rs.group())
else:
count = 0
return count
设置随机请求头,设置middlewares中内容
代码如下:
# 设置随机请求头
from fake_useragent import UserAgent
class RandomUAMiddleware(object):
def __init__(self,crawler):
super(RandomUAMiddleware, self).__init__()
self.crawler = crawler
self.ua = UserAgent()
@classmethod
def from_crawler(cls,crawler):
return cls(crawler)
# 处理请求函数
def process_request(self,request,spider):
# 随机产生请求头
request.headers.setdefault('User-Agent',self.ua.random)
在item中创建数据模型类
class BoleItem(scrapy.Item):
title = scrapy.Field()
blog_url = scrapy.Field()
img_src = scrapy.Field()
blog_date = scrapy.Field()
tags = scrapy.Field()
like_count = scrapy.Field()
comment_count = scrapy.Field()
bookmark_count = scrapy.Field()
img_path = scrapy.Field()
blog_id = scrapy.Field()
写入数据库,我们使用异步写入,首先在setting文件中自己配置一些项目信息
# 数据库配置
MYSQL_HOST = '127.0.0.1'
MYSQL_PORT = 3306
MYSQL_USER = 'root'
MYSQL_PASSWD = '123456'
MYSQL_CHARSET = 'utf8'
MYSQL_DBNAME = 'jobbole'
然后设置pipelines,其中包含了我们写入数据库的代码,和下载图片的代码
代码如下:
from scrapy.http.request import Request
class MyImagePipeline(ImagesPipeline):
# 把当前的item处理完成之后,执行这个函数
def item_completed(self, results, item, info):
# 首先判断要下载的图片是否有完成信息
if results:
try:
# 检测有有没有图片信息字典
# 先取出列表中的元组,再从元组中取出字典
img_dic = results[0][1]
img_path = img_dic["path"]
except Exception,e:
print '------',e
img_path = '没有图片路径'
else:
img_path = '没有图片信息'
# 对item的img_path赋值
item["img_path"] = 'imgs/'+img_path
# 返回item
return item
# 当需要处理媒体文件时,会执行该函数
def get_media_requests(self, item, info):
# 获取图片的url地址
for src in item['img_src']:
return Request(
url=src,
meta={'item':item}
)
# return [Request(x) for x in item["img_src"], []]
# 指定图片存放的路径
def file_path(self, request, response=None, info=None):
# 取出item
item = request.meta["item"]
# 取出图片url
img_name = request.url.split('/')[-1]
return img_name
# 异步写入数据库
from twisted.enterprise import adbapi
from MySQLdb import cursors
class MysqlTwistedPipeline(object):
@classmethod
# 这个函数会自动调用
def from_settings(cls,settings):
# 准备好连接数据库需要的参数
db_params = dict(
# 复制当前行Ctrl + d
host = settings["MYSQL_HOST"],
port = settings["MYSQL_PORT"],
user = settings["MYSQL_USER"],
passwd = settings["MYSQL_PASSWD"],
charset = settings["MYSQL_CHARSET"],
db = settings["MYSQL_DBNAME"],
use_unicode = True,
# 指定游标类型
cursorclass=cursors.DictCursor
)
# 创建连接池
# 1.要连接的名称 2.连接需要的参数
db_pool = adbapi.ConnectionPool('MySQLdb',**db_params)
# 返回当前类的对象,并且把db_pool赋值给该类的对象
return cls(db_pool)
def __init__(self,db_pool):
# 赋值
self.db_pool = db_pool
# 处理item函数
def process_item(self,item,spider):
# 把要处理的事件进行异步处理
# 1.要处理的事件函数
# 2.事件函数需要的参数
query = self.db_pool.runInteraction(self.do_insert,item)
# 执行sql出现错误信息
query.addErrback(self.handle_error,item,spider)
# 错误的原因
def handle_error(self,failure,item,spider):
print failure
# 处理插入数据库的操作
# cursor该函数是连接数据库的函数,并且放在异步去执行,cursor执行sql语句
def do_insert(self,cursor,item):
# 1.准备sql语句
sql = 'insert into bolejb(title,blog_url,img_src,blog_date,tags,like_count,comment_count,bookmark_count,img_path,blog_id)VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
# 2.用cursor游标执行sql
cursor.execute(sql, (item["title"], item["blog_url"], item["img_src"][0], item["blog_date"], item["tags"], item["like_count"],item["comment_count"], item["bookmark_count"], item["img_path"],item["blog_id"]))
最后我们需要设置我们setting文件中的内容,将我们重写的中间件在setting中进一步设置
DOWNLOADER_MIDDLEWARES = {
'BoleSpider.middlewares.RandomUAMiddleware': 1,
}
ITEM_PIPELINES = {
'BoleSpider.pipelines.MyImagePipeline': 1,
'BoleSpider.pipelines.MysqlTwistedPipeline': 2,
}
# 根据哪个属性下载图片
IMAGES_URLS_FIELD = 'img_src'
IMAGES_STORE = 'imgs'
为了方便调试,我们可以创建一个debug文件
from scrapy.cmdline import execute execute(['scrapy','crawl','bole'])