Scrapy爬取伯乐在线采用两种入库方法
scrapy基本命令
1.建立scrapy项目
scrapy startproject mybole
2.进入项目列表并在项目目录下创建爬虫文件,此处必须加上你要爬取的链接否则会报错
cd mybole
scrapy genspider jobbole jobbole.com
3.在你创建的scrapy列表中找到spiders就是你创建的爬虫文件夹jobbole.py
4.初始化的爬虫代码scrapy已帮你建立
5.现在开始进行爬取网页文章内容的提取采用xpath方法进行爬取,爬去的xpath代码可在Chrome应用商城下载xpathhelper,也可以在命令行模式下输入如下代码就可以进入shell界面
scrapy shell “此处填写具体网址”
6.完整的jobbole.py代码如下:
# -*- coding: utf-8 -*-
import scrapy
import re
from mybole.items import MyboleItem
from scrapy.http import Request#回掉函数
from urllib import parse#域名拼接
class MyjobboleSpider(scrapy.Spider):
name = 'myjobbole'
allowed_domains = ['blog.jobbole.com']
start_urls = ['http://blog.jobbole.com/all-posts/']
def parse(self, response):
# archive > div:nth-child(1) > div.post-thumb > a
post_nodes=response.css("#archive .floated-thumb .post-thumb a")
#post_urls=response.css('//div[@id="archive"]/div[@class="post floated-thumb"]/div[@class="post-thumb"]/a/@href').extract()
for post_node in post_nodes:
image_url=post_node.css("img::attr(src)").extract_first('')
post_url=post_node.css("::attr(href)").extract_first('')
#meta是个字典,意思是每次运行代码吧图片url提取
yield Request(url=parse.urljoin(response.url,post_url),meta={"front_image_url":image_url}, callback=self.parse_detial)#运行url对具体文章提取内容循环多次
#在response.css(".next.page-numbers::attr(href)")中两个class之间加了空格代表父子,不加空格代表是同一个class
next_urls=response.css(".next.page-numbers::attr(href)").extract_first('')
if next_urls:
#scrapy 根据函数名调用函数进行每页的爬取回到这个def parse(self, response):
yield Request(url=parse.urljoin(response.url,next_urls),callback=self.parse)#运行url对具体文章提取内容循环多次
细节性知识点:
#strip()去掉提取内容中的回车换行符即“\r\n”,replace(".","")替换.为空
create_date=response.xpath("//p[@class=‘entry-meta-hide-on-mobile’]/text()").extract()[0].strip().replace("·", “”)
tag_list = [tag for tag in tag_list if not tag.strip().endswith(‘评论’)]#过滤掉“评论”
tags = ‘,’.join(tag_list)
join(): 连接字符串数组。将字符串、元组、列表中的元素以指定的字符(分隔符)连接生成一个新的字符串
语法: ‘sep’.join(seq)
参数说明
sep:分隔符。可以为空
seq:要连接的元素序列、字符串、元组、字典
上面的语法即:以sep作为分隔符,将seq所有的元素合并成一个新的字符串
返回值:返回一个以分隔符sep连接各个元素后生成的字符串
7.items.py代码如下:
import scrapy
class MyboleItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title=scrapy.Field()
create_date = scrapy.Field()
url = scrapy.Field()
url_object_id = scrapy.Field() # 文章 URL 的 MD5 值
front_image_url = scrapy.Field()
front_image_path = scrapy.Field()
praisenums = scrapy.Field()
favnums = scrapy.Field()
commentnums = scrapy.Field()
content= scrapy.Field()
tags= scrapy.Field()
items.py相当于python基础知识中的字典,但是在爬去信息的时候对数据操作比较多于是便有了item方便进行数据的操作。
8.piplines.py管道文件代码:
看不懂的代码请看注释
import json
import pymysql
#from twisted.enterprise import adbapi
from twisted.enterprise import adbapi
from scrapy.pipelines.images import ImagesPipeline
class MybolePipeline(object):
def process_item(self, item, spider):
return item
#此处利用scrapy自带scrapy.pipelines.images爬取图片并将图片路径 保存
class MyboleImagePipeline(ImagesPipeline):
def item_completed(self, results, item, info):
for ok, v in results:
image_file_path = v['path']
item['front_image_path'] = image_file_path
return item
#json文件存储
class JsonWithEncodingPipeline(object):
def __init__(self):
self.file = open('article.json', 'a', encoding='utf-8')
#此处打开文件的方式“a”与“w”一样但是必须要添加encoding='utf-8'将爬取的xpath代码转换问utf-8类型
def process_item(self, item, spider):
#先将item对象转化为字典对象才能进行json文件存储, ensure_ascii=False代表不用ascii存储也就是 #用中文就好了
self.file.write(json.dumps(dict(item), ensure_ascii=False) + ',\n')
return item
def close_spider(self):
self.file.close()
#一般数据库存储
#此处代码最容易报错请确保数据库表 中开头么有空格其他注意细节
class MySQLPipeline(object):
def __init__(self):
self.conn = pymysql.connect(host="127.0.0.1",user="root",password="root",db="Articles", charset='utf8')
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
insert_sql = '''
insert into jobbole_article(title,create_date,url,url_object_id,front_img_url,
front_img_path,praise_nums,comment_nums,fav_nums,tags,content)
values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
'''
self.cursor.execute(insert_sql, (
item['title'], item['create_date'], item['url'], item['url_object_id'],
item['front_img_url'], item['front_img_path'], item['praise_nums'],
item['comment_nums'], item['fav_nums'], item['tags'], item['content']
))
self.conn.commit()
def close_spider(self,spider): #TypeError: close_spider() takes 1 positional argument but 2 were given
self.cursor.close()
self.conn.close()
#异步存入数据库目的就是爬虫速度太快数据存入数据库代码,此处异步存储加快速度分布存入,防止数
#据太多数据库堵着
class MysqlTwistedPipline(object):
#builtins.TypeError: __init__() missing 1 required positional argument: 'db_pool'
#出现如上错误在from_settinngs 前添加@classmethod
def __init__(self,dbpool):
self.dbpool=dbpool
#从settings获得 数据库配置信息
@classmethod
#@classmethod优先级高于__init__使dbpool输出便于初始化
def from_settings(cls,settings):
# 用一个db_params接收连接数据库的参数
dbparms=dict(host=settings["MYSQL_HOST"],db=settings["MYSQL_DBNAME"],
user=settings["MYSQL_USER"],password=settings["MYSQL_PASSWORD"],
charset="utf8",
#设置游标类型
cursorclass=pymysql.cursors.DictCursor,
use_unicode=True,
)
# 创建连接池
dbpool=adbapi.ConnectionPool('pymysql',**dbparms)
# 返回一个pipeline对象
return cls(dbpool)
def process_item(self,item,spider):
#把要执行的sql语句放入连接池
# 使用Twisted将mysql插入变成异步执行
# runInteraction可以将传入的函数变成异步的
query=self.dbpool.runInteraction(self.insert_into,item)
query.addErrback(self.handle_error,item,spider)
# 如果sql执行发送错误,自动回调addErrBack()函数
return item
def insert_into(self,cursor,item):
# 会从dbpool取出cursor
# 执行具体的插入
insert_sql = '''
insert into jobbole_article(title,create_date,url,url_object_id,front_img_url,
front_img_path,praise_nums,comment_nums,fav_nums,tags,content)
values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
'''
cursor.execute(insert_sql, (
item['title'], item['create_date'], item['url'], item['url_object_id'],
item['front_img_url'], item['front_img_path'], item['praise_nums'],
item['comment_nums'], item['fav_nums'], item['tags'], item['content']
))
# 拿传进的cursor进行执行,并且自动完成commit操作
def handle_error(self, failure, item, spider):
# 处理异步插入的异常
print(failure)
9.setting文件是对整个爬虫运行哪块不运行哪块的整体控制:
(1)简单来说就是不遵守robots.txt规定,不写着这句话代表你什么都获取不了
Obey robots.txt rules
ROBOTSTXT_OBEY = False
(2)管道文件控制器,数字越小 代表运行优先级越大,简单来说就是谁小先运行谁
ITEM_PIPELINES = {
#'mybole.pipelines.MybolePipeline': 300,
#'scrapy.pipelines.images.ImagesPipeline':1,
#"mybole.pipelines.JsonWithEncodingPipeline":300,
#'mybole.pipelines.MyboleImagePipeline':200,
'scrapy.pipelines.images.ImagesPipeline':1
}
import os
project_dir=os.path.dirname(os.path.abspath(__file__))#获得当前目录的名称向前取获得目录名
IMAGE_URLS_FIELD='front_image_url'#获取url
#请睁开眼你没有加S导致程序错误
# IMAGES_URLS_FIELD = 'front_image_url'
IMAGE_STORE=os.path.join(project_dir,'images')#设置图片路径
(3)获取图片利用的是自带的图片管道进行爬取,需要与管道相联合
(4)在setting 中添加数据库信息你怎样建立数据库内容随你改变
MYSQL_HOST='localhost'
MYSQL_DBNAME="Articles"
MYSQL_USER="root"
MYSQL_PASSWORD="root"
10.setting.py整体起作用代码在这如需修改请自行修改:
#当我们的item被传输到pipeline我们可以将其进行存储到数据库等工作
ITEM_PIPELINES = {
#'bole.pipelines.BolePipeline': 300,
#'bole.pipelines.JsonWithEncodingPipeline': 2,
# 'bole.pipelines.JsonWithEncodingPipeline': 200,
# "bole.pipelines.MysqlTwistedPipline":200,
# "bole.pipelines.BoleImagePipeline":100,
'scrapy.pipelines.images.ImagesPipeline':1
}
MYSQL_HOST='localhost'
MYSQL_DBNAME="Articles"
MYSQL_USER="root"
MYSQL_PASSWORD="root"
import os
# 获取项目目录
project_dir = os.path.dirname(os.path.abspath(__file__))
# 指定需要下载的图片字段,这个字段必须是可迭代对象,这里 front_img_url 就是图片字段名
IMAGES_URLS_FIELD = 'front_img_url'
# 指定图片存储路径,这里将项目目录与 images 目录进行拼接,作为图片存储路径
IMAGES_STORE = os.path.join(project_dir, 'images')
MYSQL_HOST='localhost'
MYSQL_DBNAME="Articles"
MYSQL_USER="root"
MYSQL_PASSWORD="root"