scrapy安装以及目录结构介绍

创建有python3的虚拟环境

mkvirtualenv mkvirtualenv py3env

安装scrapy

进入虚拟环境py3env,把pip的源设置为豆瓣源。这个命令执行完毕后,以后使用pip安装Python包时就会从豆瓣源下载,速度会更快

pip config set global.index-url https://pypi.doubanio.com/simple

安装scrapy依赖包lxml、twisted

安装scrapy

pip install -i https://pypi.douban.com/simple/ scrapy

补充

进入虚拟环境: workon py3env

创建项目: scrapy startproject ArticleSpider

建立spider: scrapy genspider jobbole blog.jobbple.com

PyCharm 调试scrapy 执行流程

scrapy目录结构:

  • scrapy.cfg:配置文件
  • setings.py:基本设置
    SPIDER_MODULES = ['ArticleSpider.spiders'] #存放spider的路径
    NEWSPIDER_MODULE = 'ArticleSpider.spiders' 
  • pipelines.py:做跟数据存储相关的东西
  • middilewares.py:自己定义的middlewares 定义方法,处理响应的IO操作
  • init.py:项目的初始化文件
  • items.py:定义我们所要爬取的信息的相关属性。Item对象是种类似于表单,用来保存获取到的数据

此时,pycharm下articlespiders项目还没完全配置好:

 ①需要在pycharm的setting中的project interpreter下配置python:使用虚拟环境articlespider下的python3.6

 ②爬虫项目不像Django项目,没有自动配置好调试或运行相关,需要我们手动生成一个main.py文件,作为启动文件:

在PyCharm中运行爬虫文件

在ArticleSpider文件根目录下建立main.py文件

要学会用断点和DEBUG

在实战中操作

可能出现的错误

File "C:\Users\用户名\AppData\Local\Programs\Python\Python39\lib\site-packages\cryptography\exceptions.py", line 9, in <module>
from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions
ImportError: DLL load failed while importing _rust: 找不到指定的程序。

解决方法:尝试过网上一系列解决方法,没有解决。所使用版本为Python3.9.0,换个版本问题解决。。。

xpath的用法

xpath简介

  • xpath使用路径表达式在xml和html中进行导航。
  • xpath包含标准函数库。
  • xpath是一个w3c的标准。
  • xpath速度要远远超beautifulsoup

xpath节点关系

  • 父节点
  • 子节点
  • 同胞节点
  • 先辈节点
  • 后代节点

xpath语法

表达式

说明

article

选取所有article元素的所有子节点

/article

选取根元素article

article/a

选取所有属于article的子元素的a元素

//div

选取所有div子元素(不论出现在文档任何地方)

article//div

选取所有属于article元素的后代的div元素,不管它出现在article之下的任何位置

//@class

选取所有名为class的属性

/article/div[1]

选取属于article子元素的第一个div元素

/article/div[last()]

选取属于article子元素的最后一个div元素

/article/div[last()-1]

选取属于article子元素的倒数第二个div元素

//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

选取所有属于article元素的div元素的p元素 以及 文档中所有的span元素

contains()用法

response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").extract()[0]

表示在span标签中class属性中含有 bookmark-btn 即为符合

正文保留html标签,以便后续研究

scrapy shell url 调试xpath

如果在py3中就都显示中文了

re.math(reg,html).group() #正则匹配

tag_list=['职场','2 评论','今昔'] [element for element in tag_list if not element.strip().endswith('评论')] #结果['职场', '今昔']

xpath提取元素

css选择器实现字段解析



编写spider完成抓取过程

parse.urljoin(url,post_url)的使用

实现模拟登陆(undetected_chromedriver)

上处代码使用undetected_chromedriver时会自动识别Chrome浏览器版本并自动下载对应的Chromedriver,运行时总是下载失败一直未解决,为了后续项目进行,采用selenium模拟登录,登录时会被cnblog反爬识别,采用qq登录即可

from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://account.cnblogs.com/signin")

完成页面爬取

提取详情页信息

item的使用

数据爬取的任务就是从非结构的数据中提取出结构性的数据。
items 可以让我们自定义自己的字段(类似于字典,但比字典的功能更齐全)
当我们爬取数据,获  取数据后需要将数据传给下个函数调用,则可以利用scrapy request下的meta属性,meta接收的是字典类型,当使用request爬取数据时,将需要传递给下个函数的数据放到meta中,meta中的数据会传给下个函数的response中去,可以被下个函数使用

关于scrapy request的meta:

Request中meta参数的作用是传递信息给下一个函数,使用过程可以理解成:

把需要传递的信息赋值给这个叫meta的变量,
但meta只接受字典类型的赋值,因此
要把待传递的信息改成“字典”的形式,即:
meta={'key1':value1,'key2':value2} 如果想在下一个函数中取出value1,
只需得到上一个函数的meta['key1']即可,
因为meta是随着Request产生时传递的,
下一个函数得到的Response对象中就会有meta,
即response.meta, meta是一个dict,主要是用解析函数之间传递值,一种常见的情况:在parse中给item某些字段提取了值,但是另外一些值需要在parse_item中提取,这时候需要将parse中的item传到parse_item方法中处理,显然无法直接给parse_item设置而外参数。 Request对象接受一个meta参数,一个字典对象,同时Response对象有一个meta属性可以取到相应request传过来的meta
request meta

request meta

关于urllib parse.urljoin(url1,url2):

parse.url:能将相对路径,自动补充域名补全路径,前提主域名response能获取域名路径

一般爬取网上数据时,存在网页某些url是相对路径形式展示的,通过js代码等操作补全,此时我们要爬取完整url路径,就需要用到parse.urljoin方法了

如果你是相对路径,没有补全域名,我就从response里取出来补全,如果你有域名我则不起作用

items.py创建类

在jobbole.py parse_detail 中实例化 JobBoleArticleItem , 并将爬取到的数据放入 JobBoleArticleItem 对象中:

setting的相关设置

robot协议:目标网站禁止爬取的连接目录

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

pipelines管道

ITEM_PIPELINES:item的管道,配置好ArticlespiderPipeline路径,当执行代码 yield article_item 时,程序会转到pipelines.py文件中执行  ArticlespiderPipeline 类,
也就是通过 yield article_item 将article_item数据传给pipelines调用。并执行pipelines中的相关类,如: ArticlespiderPipeline  ,在 ArticlespiderPipeline 类中,我们可以通过代码编写,将数据存到数据库中去

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
"ArticleSpider.pipelines.ArticlespiderPipeline": 300,
}

ITEM_PIPELINES

pipelines.py文件代码

class ArticlespiderPipeline:
def process_item(self, item, spider):
return item

pipelines.py

scrapy配置图片下载

配置setting.py

配置pipeline

ITEM_PIPELINES = { "ArticleSpider.pipelines.ArticlespiderPipeline": 300, "scrapy.pipelines.images.ImagesPipeline": 1 }

配置images文件夹的路径:

Scrapy中的pipelines提供了默认的文件、图片、媒体等下载保存方式,如上所述,将路径配置上去就会相应的执行scrapy/pipelines的相关函数 ,

另外,ITEM_PIPELINES是一个数据管道的登记表,每一项后面的具体数字代表它的优先级,数字越小,越早进入管道执行

接下来我们新建一个images文件夹用于保存图片,在setting中配置images文件夹的路径:

import sys
import os
project_dir = os.path.dirname(os.path.abspath(__file__))
IMAGES_STORE = os.path.join(project_dir, "images"):

指定某字段(我们获取图片路径的字段)为我们的图片文件处理

IMAGES_URLS_FIELD = "front_image_url"   # 指定爬取的某(图片)字段作为图片处理

若报错ModuleNotFoundError:No module named 'PIL',是因为图片保存等需要pillow库来操作,解决方法在虚拟环境中安装pillow:pip install pillow

若报错ValueError:Missing scheme in request url:h

原因是'scrapy.pipelines.images.ImagesPipeline' 中对图片的要求是数组格式,而我们目前传递的图片类型(front_image_url)是字符串格式,因此我们需要将爬取的图片类型改成数组类型获取

article_item["front_image_url"] = front_image_url
# ↓改成数组形式
article_item["front_image_url"] = [front_image_url]

额外知识:

 scrapy下的pipelines目录下有files.py、images.py、media.py ,用于处理文件、图片、用户上传下载媒体等数据

IMAGES_URLS_FIELD = "front_image_url"  # 指定该字段作为图片处理对象

IMAGES_STORE = os.path.join(project_dir, 'images')  # 图片存放路径

IMAGES_MIN_HEIGHT = 100  # 图片最小高度
IMAGES_MIN_WIDTH = 100 # 图片最小宽度

 使用scrapy自带的image.py处理图片,只需要按要求命名一致,以及在ITEM_PIPELINES 配置好所使用的类即可:ImagesPipeline , 

使用files.py、media.py也是一样的道理。

数据保存相关

保存到本地json文件中

方式一:

在pipelines.py文件中新建类:JsonWithEncodingPipeline

打开文件时不直接使用 open ,用python自带的codecs包,可以避免一些编码方面的问题出现

使用json.dumps时,不使用ensure_ascii=False ,默认输出中文是ASCII字符码,要想正确输出中文,需要指定ensure_ascii=False

pipelines.py/JsonWithEncodingPipelines:

class JsonWithEncodingPipeline(object):
# 自定义json文件的导出
def __init__(self):
self.file = codecs.open('article.json', 'w', 'utf-8')
def process_item(self, item, spider):
# 将数据转换为字符串
lines = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(lines)
return item
def spider_closed(self, spider):
self.file.close()

在setting中配置ITEM_PIPELINES:

ITEM_PIPELINES = {
'ArticleSpider.pipelines.JsonWithEncodingPipeline': 2,
}

当爬虫爬取数据,保存到 item 中,经过yield item ,转到pipelines中执行里面的类

方式二:

使用scrapy中自带的类保存文件:

# scrapy/exporters

['BaseItemExporter', 'PprintItemExporter', 'PickleItemExporter','CsvItemExporter', 'XmlItemExporter','JsonLinesItemExporter','JsonItemExporter',   # 保存json文件 'MarshalItemExporter']
from scrapy.exporters import JsonItemExporter

class JsonExporterPipeline(object):
#调用scrapy提供的json export导出json文件
def __init__(self):
self.file = open('articleexport.json', 'wb')
self.exporter = JsonItemExporter(self.file, encoding="utf-8", ensure_ascii=False)
self.exporter.start_exporting() def process_item(self, item, spider):
self.exporter.export_item(item)
return item def spider_closed(self, spider):
self.exporter.finish_exporting()
self.file.close()

在setting中配置ITEM_PIPELINES:

ITEM_PIPELINES = {
  "ArticleSpider.pipelines.JsonExporterPipeline": 3,
}

将数据保存到mysql中

 在Navicat中新建数据库article_spider ,再新建表jobbole_article,填充数据类型

在虚拟环境下安装:mysqlclient

pip install mysqlclient

开始我们的数据保存到mysql操作

方式一: 利用pipelines保存数据到数据库(同步)

import MySQLdb
class MysqlPipeline(object):
# 数据保存
def __init__(self):
"""
初始化构造函数,连接数据库
"""
self.conn = MySQLdb.connect("127.0.0.1", "root", "123456", "article_spider", charset="utf8", use_unicode=True)
self.cursor = self.conn.cursor()# 创建游标 def process_item(self, item, spider):
"""
处理数据
"""
insert_sql = """
insert into jobbole_article(title, url, url_object_id, front_image_url, front_image_path, parise_nums, comment_nums, fav_nums, tags, content, create_date)
values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
params = list()
params.append(item.get("title", ""))
params.append(item.get("url", ""))
params.append(item.get("url_object_id", ""))
# 将list[]转换成字符串
front_image = ",".join(item.get("front_image_url", []))
params.append(item.get(front_image))
params.append(item.get("front_image_path", ""))
params.append(item.get("parise_nums", 0))
params.append(item.get("content_nums", 0))
params.append(item.get("fav_nums", 0))
params.append(item.get("tags", ""))
params.append(item.get("content", ""))
params.append(item.get("create_date", "1970-01-01"))
self.cursor.execute(insert_sql, (params))
self.conn.commit() return item

在setting中配置ITEM_PIPELINES:

ITEM_PIPELINES = {
  "ArticleSpider.pipelines.MysqlPipeline": 4,
}

方式二:利用pipelines保存数据到数据库(twisted异步)

因为我们的爬取速度可能大于数据库存储的速度,最好是异步操作(异步化操作mysql)

在setting.py中配置数据库连接的相关配置参数:

MYSQL_HOST = "127.0.0.1" 
MYSQL_DBNAME = "article_spider"
MYSQL_USER = "root"
MYSQL_PASSWORD = "123456"

在pipelines.py中新建类:MysqlTwistedPipline 执行顺序:from_settings → init → pross_item

import MySQLdb
import MySQLdb.cursors
from twisted.enterprise import adbapi
class MySQLTwistedPipeline(object):
def __init__(self, dbpool):
self.dbpool = dbpool # 重载方法
@classmethod
def from_settings(cls, settings):
dbparms = dict(
host=settings["MYSQL_HOST"],
db=settings["MYSQL_DBNAME"],
user=settings["MYSQL_USER"],
passwd=settings["MYSQL_PASSWORD"],
charset="utf8",
cursorclass=MySQLdb.cursors.DictCursor,
use_unicode=True,
)
# 连接池
dbpool = adbapi.ConnectionPool("MySQLdb", **dbparms)
return cls(dbpool) def process_item(self, item, spider):
# 将某个方法扔到池里运行
query = self.dbpool.runInteraction(self.do_insert,item)
# 报错的处理方法
query.addErrback(self.handle_error, item, spider) def handle_error(self, failure, item, spider):
print(failure)
def do_insert(self, cursor, item):
"""
入库逻辑
"""
insert_sql = """
insert into jobbole_article(title, url, url_object_id, front_image_url, front_image_path, parise_nums, comment_nums, fav_nums, tags, content, create_date)
values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
params = list()
params.append(item.get("title", ""))
params.append(item.get("url", ""))
params.append(item.get("url_object_id", ""))
# 将list[]转换成字符串
front_image = ",".join(item.get("front_image_url", []))
params.append(item.get(front_image))
params.append(item.get("front_image_path", ""))
params.append(item.get("parise_nums", 0))
params.append(item.get("content_nums", 0))
params.append(item.get("fav_nums", 0))
params.append(item.get("tags", ""))
params.append(item.get("content", ""))
params.append(item.get("create_date", "1970-01-01")) cursor.execute(insert_sql, tuple(params))

在setting中配置ITEM_PIPELINES:

ITEM_PIPELINES = {
  "ArticleSpider.pipelines.MysqlTwistedPipeline": 4,
}

运行后出错AttributeError: module 'MySQLdb' has no attribute 'cursors'

解决方法:修改from_setting方法

@classmethod
def from_settings(cls, settings):
from MySQLdb.cursors import DictCursor
dbparms = dict(
host=settings["MYSQL_HOST"],
db=settings["MYSQL_DBNAME"],
user=settings["MYSQL_USER"],
passwd=settings["MYSQL_PASSWORD"],
charset="utf8",
cursorclass=DictCursor,
use_unicode=True,
)
# 连接池
dbpool = adbapi.ConnectionPool("MySQLdb", **dbparms)
return cls(dbpool)

问题:数据插入时遇到主键冲突问题

解决方法:插入值的时候更新字段

values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ON DUPLICATE KEY UPDATE parise_nums = VALUES(parise_nums)

scrapy提供item loader机制提取信息

# 在parse_detail方法中配置
item_loader = ItemLoader(item=JobBoleArticleItem(), response=response)
item_loader.add_css("title", "#news_title a::text")
item_loader.add_css("create_date", "#news_info .time::text")
item_loader.add_css("content", "#news_content")
item_loader.add_css("tags", "news_tags a::text")
item_loader.add_value("url", response.url)
item_loader.add_value("front_image_url", response.meta.get('front_image_url', ""))
# 加载item
article_item = item_loader.load_item()

MapCompose,TakeFirst的使用

Scrapy爬取知名技术文章网站的更多相关文章

  1. Python3.6+Scrapy爬取知名技术文章网站

    爬取分析 伯乐在线已经提供了所有文章的接口,还有下一页的接口,所有我们可以直接爬取一页,再翻页爬. 环境搭建 Windows下安装Python: http://www.cnblogs.com/0bug ...

  2. 第4章 scrapy爬取知名技术文章网站(2)

    4-8~9 编写spider爬取jobbole的所有文章 # -*- coding: utf-8 -*- import re import scrapy import datetime from sc ...

  3. 第4章 scrapy爬取知名技术文章网站(1)

    4-1 scrapy安装以及目录结构介绍 安装scrapy可以看我另外一篇博文:Scrapy的安装--------Windows.linux.mac等操作平台,现在是在虚拟环境中安装可能有不同. 1. ...

  4. 用scrapy爬取亚马逊网站项目

    这次爬取亚马逊网站,用到了scrapy,代理池,和中间件: spiders里面: # -*- coding: utf-8 -*- import scrapy from scrapy.http.requ ...

  5. 使用scrapy爬取jian shu文章

    settings.py中一些东西的含义可以看一下这里 python的scrapy框架的使用 和xpath的使用 && scrapy中request和response的函数参数 & ...

  6. 爬虫框架之Scrapy——爬取某招聘信息网站

    案例1:爬取内容存储为一个文件 1.建立项目 C:\pythonStudy\ScrapyProject>scrapy startproject tenCent New Scrapy projec ...

  7. 第5章 scrapy爬取知名问答网站

    第五章感觉是第四章的练习项目,无非就是多了一个模拟登录. 不分小节记录了,直接上知识点,可能比较乱. 1.常见的httpcode: 2.怎么找post参数? 先找到登录的页面,打开firebug,输入 ...

  8. Scrapy爬取伯乐在线文章

    首先搭建虚拟环境,创建工程 scrapy startproject ArticleSpider cd ArticleSpider scrapy genspider jobbole blog.jobbo ...

  9. scrapy爬取伯乐在线文章数据

    创建项目 切换到ArticleSpider目录下创建爬虫文件 设置settings.py爬虫协议为False 编写启动爬虫文件main.py

  10. 一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等

    本文旨在通过爬取一系列博客网站技术文章的实践,介绍一下scrapy这个python语言中强大的整站爬虫框架的使用.各位童鞋可不要用来干坏事哦,这些技术博客平台也是为了让我们大家更方便的交流.学习.提高 ...

随机推荐

  1. [FE] G2Plot 在 Vue 中使用 CDN 方式避免构建时增大 js 体积

    使用 npm.yarn 方式安装的包,虽方便使用,但是会极大增加 vendor.xx.js 体积,拖慢网站运行速度. 以 G2Plot 为例,实际在 build 构建时,会下载一些额外字体到 vend ...

  2. 我第一个开源AI小产品-video2blog即将正式发布

    前言 首先它是为了解决我自己的个人问题.不管能不能帮到你,或者对于看到的你是否有点利用价值,也没太大的关系,最起码你可以来看看我开发小产品的整个过程. 一段时间以来,我开始通过youtube平台来获取 ...

  3. Phpstrom开发工具Sftp的使用

  4. WEB服务与NGINX(12)-NGINX的变量

    目录 1. nginx的变量 1.1 内置变量 1.2 自定义变量 1. nginx的变量 nginx的变量可以在配置文件中引用,作为功能判断或日志等场景使用,变量可以分为内置变量和自定义变量. 内置 ...

  5. Git基本操作命令大全

    一.全局配置命令 ## 配置级别: –local(默认,高级优先):只影响本地仓库 –global(中优先级):只影响所有当前用户的git仓库 –system(低优先级):影响到全系统的git仓库 # ...

  6. spring-boot集成Quartz-job存储方式二RAM

    简单区分: RAM:程序启动从数据库中读取原始job配置(也可以从配置文件中读取),job中间运行过程在RAM内存中,程序停止或重启后,RAM中数据丢失,再次启动的时候会重新读取job配置.适合于单机 ...

  7. CSS布局概念与技术教程

    以下是一份CSS布局学习大纲,它涵盖了基本到高级的CSS布局概念和技术 引言 欢迎来到CSS教程!如果你已经掌握了HTML的基础知识,那么你即将进入一个全新的世界,通过学习CSS(Cascading ...

  8. 4G 信令中的 PCO 字段

    目录 文章目录 目录 Protocol Configuration Option Protocol Configuration Option PCO(Protocol Configuration Op ...

  9. 2024-05-15:用go语言,考虑一个整数 k 和一个整数 x。 对于一个数字 num, 在其二进制表示中, 从最低有效位开始, 我们计算在 x,2x,3x 等位置处设定位的数量来确定其价值。

    2024-05-15:用go语言,考虑一个整数 k 和一个整数 x. 对于一个数字 num, 在其二进制表示中, 从最低有效位开始, 我们计算在 x,2x,3x 等位置处设定位的数量来确定其价值. 举 ...

  10. 在 JS 中调整 canvas 里的文字间距

    实现说明: 在 JS 中 canvas 原生没有支持对文字间距的调整,我们可以通过将文字的每个字符单独渲染来实现.本案例从 CanvasRenderingContext2D 对象的原型链上扩展了一个用 ...