- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章一步步教你用python的scrapy编写一个爬虫由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
介绍 。
本文将介绍我是如何在python爬虫里面一步一步踩坑,然后慢慢走出来的,期间碰到的所有问题我都会详细说明,让大家以后碰到这些问题时能够快速确定问题的来源,后面的代码只是贴出了核心代码,更详细的代码暂时没有贴出来.
流程一览 。
首先我是想爬某个网站上面的所有文章内容,但是由于之前没有做过爬虫(也不知道到底那个语言最方便),所以这里想到了是用python来做一个爬虫(毕竟人家的名字都带有爬虫的含义),我这边是打算先将所有从网站上爬下来的数据放到elasticsearch里面, 选择elasticsearch的原因是速度快,里面分词插件,倒排索引,需要数据的时候查询效率会非常好(毕竟爬的东西比较多),然后我会将所有的数据在elasticsearch的老婆kibana里面将数据进行可视化出来,并且分析这些文章内容,可以先看一下预期可视化的效果(上图了),这个效果图是kibana6.4系统给予的帮助效果图(就是说你可以弄成这样,我也想弄成这样)。后面我会发一个dockerfile上来(现在还没弄).
环境需求 。
这些东西可以去找相应的教程安装,我这里只有elasticsearch的安装 。
第一步,使用python的pip来安装需要的插件(第一个坑在这儿) 。
1.tomd:将html转换成markdown 。
1
|
pip3 install tomd
|
2.redis:需要python的redis插件 。
1
|
pip3 install redis
|
3.scrapy:框架安装(坑) 。
1、首先我是像上面一样执行了 。
1
|
pip3 install scrapy
|
2、然后发现缺少gcc组件 error: command 'gcc' failed with exit status 1 。
3、然后我就找啊找,找啊找,最后终于找到了正确的解决方法(期间试了很多错误答案)。最终的解决办法就是使用yum来安装python34-devel, 这个python34-devel根据你自己的python版本来,可能是python-devel,是多少版本就将中间的34改成你的版本, 我的是3.4.6 。
1
|
yum install python34
-
devel
|
4、安装完成过后使用命令 scrapy 来试试吧.
第二步,使用scrapy来创建你的项目 。
输入命令scrapy startproject scrapydemo, 来创建一个爬虫项目 。
1
2
3
4
5
6
7
8
|
liaochengdemacbook
-
pro:scrapy liaocheng$ scrapy startproject scrapydemo
new scrapy project
'scrapydemo'
, using template directory
'/usr/local/lib/python3.7/site-packages/scrapy/templates/project'
, created
in
:
/
users
/
liaocheng
/
script
/
scrapy
/
scrapydemo
you can start your first spider with:
cd scrapydemo
scrapy genspider example example.com
liaochengdemacbook
-
pro:scrapy liaocheng$
|
使用genspider来生成一个基础的spider,使用命令scrapy genspider demo juejin.im, 后面这个网址是你要爬的网站,我们先爬自己家的 。
1
2
3
|
liaochengdemacbook
-
pro:scrapy liaocheng$ scrapy genspider demo juejin.im
created spider
'demo'
using template
'basic'
liaochengdemacbook
-
pro:scrapy liaocheng$
|
查看生成的目录结构 。
第三步,打开项目,开始编码 。
查看生成的的demo.py的内容 。
1
2
3
4
5
6
7
8
9
10
11
|
# -*- coding: utf-8 -*-
import
scrapy
class
demospider(scrapy.spider):
name
=
'demo'
## 爬虫的名字
allowed_domains
=
[
'juejin.im'
]
## 需要过滤的域名,也就是只爬这个网址下面的内容
start_urls
=
[
'https://juejin.im/post/5c790b4b51882545194f84f0'
]
## 初始url链接
def
parse(
self
, response):
## 如果新建的spider必须实现这个方法
pass
|
可以使用第二种方式,将start_urls给提出来 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# -*- coding: utf-8 -*-
import
scrapy
class
demospider(scrapy.spider):
name
=
'demo'
## 爬虫的名字
allowed_domains
=
[
'juejin.im'
]
## 需要过滤的域名,也就是只爬这个网址下面的内容
def
start_requests(
self
):
start_urls
=
[
'http://juejin.im/'
]
## 初始url链接
for
url
in
start_urls:
# 调用parse
yield
scrapy.request(url
=
url, callback
=
self
.parse)
def
parse(
self
, response):
## 如果新建的spider必须实现这个方法
pass
|
编写articleitem.py文件(item文件就类似java里面的实体类) 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import
scrapy
class
articleitem(scrapy.item):
## 需要实现scrapy.item文件
# 文章id
id
=
scrapy.field()
# 文章标题
title
=
scrapy.field()
# 文章内容
content
=
scrapy.field()
# 作者
author
=
scrapy.field()
# 发布时间
createtime
=
scrapy.field()
# 阅读量
readnum
=
scrapy.field()
# 点赞数
praise
=
scrapy.field()
# 头像
photo
=
scrapy.field()
# 评论数
commentnum
=
scrapy.field()
# 文章链接
link
=
scrapy.field()
|
编写parse方法的代码 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
def
parse(
self
, response):
# 获取页面上所有的url
nextpage
=
response.css(
"a::attr(href)"
).extract()
# 遍历页面上所有的url链接,时间复杂度为o(n)
for
i
in
nextpage:
if
nextpage
is
not
none:
# 将链接拼起来
url
=
response.urljoin(i)
# 必须是掘金的链接才进入
if
"juejin.im"
in
str
(url):
# 存入redis,如果能存进去,就是一个没有爬过的链接
if
self
.insertredis(url)
=
=
true:
# dont_filter作用是是否过滤相同url true是不过滤,false为过滤,我们这里只爬一个页面就行了,不用全站爬,全站爬对对掘金不是很友好,我么这里只是用来测试的
yield
scrapy.request(url
=
url, callback
=
self
.parse,headers
=
self
.headers,dont_filter
=
false)
# 我们只分析文章,其他的内容都不管
if
"/post/"
in
response.url
and
"#comment"
not
in
response.url:
# 创建我们刚才的articleitem
article
=
articleitem()
# 文章id作为id
article[
'id'
]
=
str
(response.url).split(
"/"
)[
-
1
]
# 标题
article[
'title'
]
=
response.css(
"#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > h1::text"
).extract_first()
# 内容
parameter
=
response.css(
"#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > div.article-content"
).extract_first()
article[
'content'
]
=
self
.parsetomarkdown(parameter)
# 作者
article[
'author'
]
=
response.css(
"#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > div:nth-child(6) > meta:nth-child(1)::attr(content)"
).extract_first()
# 创建时间
createtime
=
response.css(
"#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > div.author-info-block > div > div > time::text"
).extract_first()
createtime
=
str
(createtime).replace(
"年"
,
"-"
).replace(
"月"
,
"-"
).replace(
"日"
,"")
article[
'createtime'
]
=
createtime
# 阅读量
article[
'readnum'
]
=
int
(
str
(response.css(
"#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > div.author-info-block > div > div > span::text"
).extract_first()).split(
" "
)[
1
])
# 点赞数
article[
'badge'
]
=
response.css(
"#juejin > div.view-container > main > div > div.article-suspended-panel.article-suspended-panel > div.like-btn.panel-btn.like-adjust.with-badge::attr(badge)"
).extract_first()
# 评论数
article[
'commentnum'
]
=
response.css(
"#juejin > div.view-container > main > div > div.article-suspended-panel.article-suspended-panel > div.comment-btn.panel-btn.comment-adjust.with-badge::attr(badge)"
).extract_first()
# 文章链接
article[
'link'
]
=
response.url
# 这个方法和很重要(坑),之前就是由于执行yield article, pipeline就一直不能获取数据
yield
article
# 将内容转换成markdown
def
parsetomarkdown(
self
, param):
return
tomd.tomd(
str
(param)).markdown
# url 存入redis,如果能存那么就没有该链接,如果不能存,那么就存在该链接
def
insertredis(
self
, url):
if
self
.redis !
=
none:
return
self
.redis.sadd(
"articleurllist"
, url)
=
=
1
else
:
self
.redis
=
self
.redisconnection.getclient()
self
.insertredis(url)
|
编写pipeline类,这个pipeline是一个管道,可以将所有yield关键字返回的数据都交给这个管道处理,但是需要在settings里面配置一下pipeline才行 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
from
elasticsearch
import
elasticsearch
class
articlepipelines(
object
):
# 初始化
def
__init__(
self
):
# elasticsearch的index
self
.index
=
"article"
# elasticsearch的type
self
.
type
=
"type"
# elasticsearch的ip加端口
self
.es
=
elasticsearch(hosts
=
"localhost:9200"
)
# 必须实现的方法,用来处理yield返回的数据
def
process_item(
self
, item, spider):
# 这里是判断,如果是demo这个爬虫的数据才处理
if
spider.name !
=
"demo"
:
return
item
result
=
self
.checkdocumentexists(item)
if
result
=
=
false:
self
.createdocument(item)
else
:
self
.updatedocument(item)
# 添加文档
def
createdocument(
self
, item):
body
=
{
"title"
: item[
'title'
],
"content"
: item[
'content'
],
"author"
: item[
'author'
],
"createtime"
: item[
'createtime'
],
"readnum"
: item[
'readnum'
],
"praise"
: item[
'praise'
],
"link"
: item[
'link'
],
"commentnum"
: item[
'commentnum'
]
}
try
:
self
.es.create(index
=
self
.index, doc_type
=
self
.
type
,
id
=
item[
"id"
], body
=
body)
except
:
pass
# 更新文档
def
updatedocument(
self
, item):
parm
=
{
"doc"
: {
"readnum"
: item[
'readnum'
],
"praise"
: item[
'praise'
]
}
}
try
:
self
.es.update(index
=
self
.index, doc_type
=
self
.
type
,
id
=
item[
"id"
], body
=
parm)
except
:
pass
# 检查文档是否存在
def
checkdocumentexists(
self
, item):
try
:
self
.es.get(
self
.index,
self
.
type
, item[
"id"
])
return
true
except
:
return
false
|
第四步,运行代码查看效果 。
使用scrapy list查看本地的所有爬虫 。
1
2
3
|
liaochengdemacbook
-
pro:scrapydemo liaocheng$ scrapy
list
demo
liaochengdemacbook
-
pro:scrapydemo liaocheng$
|
使用scrapy crawl demo来运行爬虫 。
1
|
scrapy crawl demo
|
到kibana里面看爬到的数据,执行下面的命令可以看到数据 。
1
2
3
4
5
6
|
get
/
article
/
_search
{
"query"
: {
"match_all"
: {}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
{
"took"
:
7
,
"timed_out"
: false,
"_shards"
: {
"total"
:
5
,
"successful"
:
5
,
"skipped"
:
0
,
"failed"
:
0
},
"hits"
: {
"total"
:
1
,
"max_score"
:
1
,
"hits"
: [
{
"_index"
:
"article2"
,
"_type"
:
"type"
,
"_id"
:
"5c790b4b51882545194f84f0"
,
"_score"
:
1
,
"_source"
: {}
}
]
}
}
|
总结 。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我的支持.
原文链接:https://juejin.im/post/5c88bb19f265da2d96184df3 。
最后此篇关于一步步教你用python的scrapy编写一个爬虫的文章就讲到这里了,如果你想了解更多关于一步步教你用python的scrapy编写一个爬虫的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
与其他许多不同,Coq 接受一个可选的显式参数,该参数可用于指示固定点定义的递减结构。 从 Gallina 规范,1.3.4, Fixpoint ident params {struct ident0
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
前言:我目前正在学习 ANN,因为我在大约 83 个类别中有大约 18500 张图像。它们将用于训练 ANN 以实时识别大致相等的图像。我按照书中的图像示例进行操作,但它对我不起作用。所以我要回到开头
在我的程序中,我处理有时为 NULL 的 C 风格字符串(类型为 char *)。我想教 cout 优雅地处理这些问题(即打印“(null)”而不是段错误)。 我的第一次尝试: ostream& op
我正在开发一个采用(定制的)微线程解决方案的大型程序。有时我需要调试崩溃。在这种时候,能够从一个微线程切换到另一个微线程是很有用的。 如果我正在进行实时调试,我可以将所有寄存器替换为来自微线程上下文的
我可以教 Notepad++ 在它看到多行注释时应用折叠,其中注释以井号开头,多行注释是连续行上的井号吗? # This is a comment # It continues on the next
我被要求为一个 child 辅导 Pascal。尽管在我设法获得教程之前从未见过 Pascal,但我现在知道的足以教他了。 我给你们写信是想看看是否有人能给我指出一些涉及简单算法的基本练习,比如:对这
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在维护来自外国情报监视法庭的大量编辑文件的文件。 它们带有大段文本,如下所示: 当 OCR 尝试处理此问题时,您会收到如下文本: production of this data on a dail
今年夏天,我将教高中生使用 C++ 编程。我必须在一周内教给他们 Material 。通读了公司给我的教程,他们建议我一开始就在程序中使用“include stdafx.h”。 你觉得呢 includ
我需要在他的笔记本电脑上安装一个 c# ide(免费),我需要下载 sdk 还是 windows 7 带有 c# 编译器? (从头开始设置已经有一段时间了) 最佳答案 你可以试试Visual C# 2
背景 我刚刚在 AWS 上启动了一个新的 Redshift 实例,我可以毫无问题地通过 psql cli 客户端连接到它。 问题 我正在尝试让我的 Rails 3 应用程序连接到 Redshift 盒
我是一名优秀的程序员,十分优秀!