Scrapy项目练习
中文文档:http://scrapy-chs.readthedocs.io/zh_CN/latest/intro/tutorial.html#intro-tutorial
官网文档:https://doc.scrapy.org/en/latest/
项目需求:
爬取伯乐在线的最新文章里的文章,因为有下一页的按钮可以通过下一页的选项来获取所有文章

新建项目
scrapy startproject ArticleSpider
创建爬虫
cd ArticleSpider\ scrapy genspider jobbole blog.jobbole.com
可以用以下命令启动爬虫(在ArticleSpider目录下)
scrapy crawl jobbole
用pycharm打开项目

jobbole.py就是我们写爬虫代码的地方,默认是下面创建好后,jobbole.py里面的内容这样的
# -*- coding: utf-8 -*-
import scrapy
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']
start_urls = ['http://blog.jobbole.com/']
def parse(self, response):
pass
其中start_urls就是我们要爬取的url。类JobbleSpider继承了scrapy.Spider我打开简单看一下这里面写了什么(ctrl+鼠标左键点击)
一眼就看到Spider类的下面这些方法
class Spider(object_ref):
# 此处省略
def start_requests(self):
cls = self.__class__
if method_is_overridden(cls, Spider, 'make_requests_from_url'):
warnings.warn(
"Spider.make_requests_from_url method is deprecated; it "
"won't be called in future Scrapy releases. Please "
"override Spider.start_requests method instead (see %s.%s)." % (
cls.__module__, cls.__name__
),
)
for url in self.start_urls:
yield self.make_requests_from_url(url)
else:
for url in self.start_urls:
yield Request(url, dont_filter=True)
def make_requests_from_url(self, url):
""" This method is deprecated. """
return Request(url, dont_filter=True)
def parse(self, response):
raise NotImplementedError
......
可以知道,scrapy调用下载器为我们下载url的内容,之后把结果给了parse,我们需将爬虫代码写在parse下即可,后面再对整个流程做进行更详细的分析。
在项目目录下创建一个用于执行命令的文件main.py,如下

# -*- coding: utf-8 -*- from scrapy.cmdline import execute import sys import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # 工程目录D:\ArticleSpider sys.path.append(BASE_DIR) # 设置scrapy命令在此目录下运行 execute(["scrapy", "crawl", "jobbole"]) # 配置执行爬虫的命令
在写爬虫之前,我们将会对将会用到的xpath语法做一下简单的介绍
xpath使用路径表达式在xml和html中进行导航,获取其中的元素。
xpath包含标准函数库
xpath是一个w3c的标准,因此不管什么库支持xpath的时候,其语法是一致的,所以说她的应用也是非常广的,值得一学。
xpath语法

xpath的定位用法

其他(第三项说明里的title改成div)

好了,开始写代码
改一下url,改成具体文章的url
start_urls = ['http://blog.jobbole.com/110287/']
在parse方法里写代码
先找文章标题

xpath可以通过鼠标复制(谷歌浏览器)

得到的路径是://*[@id="post-110287"]/div[1]/h1
def parse(self, response):
re_selector = response.xpath('//*[@id="post-110287"]/div[1]/h1')
pass
在pass那里做一饿断点进入main.py里debug一下

要获取文本内容直接在xpath路径后面加/text()
re_selector = response.xpath('//*[@id="post-110287"]/div[1]/h1/text()')
我们上面的每一次调试都请求了一次url,请求的有点慢,是比较浪费时间的,实际上scrapy给我们提供了一个用于调试的scrapy shell
在命令行中输入(工程目录下)
scrapy shell http://blog.jobbole.com/110287/

获取标题
title = response.xpath('//div[@class="entry-header"]/h1/text()').extract_first("")
获取创建时间
create_date = response.xpath("//p[@class='entry-meta-hide-on-mobile']/text()").extract()[0].strip().replace("·","").strip()
获取点赞数
praise_nums = response.xpath("//span[contains(@class, 'vote-post-up')]/h10/text()").extract()[0]
获取收藏和评论,这个有点不同,我们只要数字,不要文字

fav_nums = response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").extract()[0]
match_re = re.match(".*?(\d+).*", fav_nums)
if match_re:
fav_nums = match_re.group(1)
comment_nums = response.xpath("//a[@href='#article-comment']/span/text()").extract()[0]
match_re = re.match(".*?(\d+).*", comment_nums)
if match_re:
comment_nums = match_re.group(1)
获得正文
content = response.xpath("//div[@class='entry']").extract()[0]
获得标签,这个也有点不同,我们要标签但是如果有评论,这个评论我们是不要的

tag_list = response.xpath("//p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
tag_list = [element for element in tag_list if not element.strip().endswith("评论")]
tags = ",".join(tag_list)
最后写完就是下面这样(extract_first("")表示,如果提取不到列表里的第一个值就返回空,如用[0]提取不到会报错)
# -*- coding: utf-8 -*-
import scrapy
import re
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']
start_urls = ['http://blog.jobbole.com/110287/']
def parse(self, response):
# 提取文章的具体字段
title = response.xpath('//div[@class="entry-header"]/h1/text()').extract_first("")
create_date = response.xpath("//p[@class='entry-meta-hide-on-mobile']/text()").extract()[0].strip().replace("·",
"").strip()
praise_nums = response.xpath("//span[contains(@class, 'vote-post-up')]/h10/text()").extract()[0]
fav_nums = response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").extract()[0]
match_re = re.match(".*?(\d+).*", fav_nums)
if match_re:
fav_nums = match_re.group(1)
comment_nums = response.xpath("//a[@href='#article-comment']/span/text()").extract()[0]
match_re = re.match(".*?(\d+).*", comment_nums)
if match_re:
comment_nums = match_re.group(1)
content = response.xpath("//div[@class='entry']").extract()[0]
tag_list = response.xpath("//p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
tag_list = [element for element in tag_list if not element.strip().endswith("评论")]
tags = ",".join(tag_list)
pass
下面来认识一下css选择器


上面的xpath的操作实际上都可以替换成css选择器

# 通过css选择器提取字段
front_image_url = response.meta.get("front_image_url", "") # 文章封面图
title = response.css(".entry-header h1::text").extract()[0]
create_date = response.css("p.entry-meta-hide-on-mobile::text").extract()[0].strip().replace("·", "").strip()
praise_nums = response.css(".vote-post-up h10::text").extract()[0]
fav_nums = response.css(".bookmark-btn::text").extract()[0]
match_re = re.match(".*?(\d+).*", fav_nums)
if match_re:
fav_nums = int(match_re.group(1))
else:
fav_nums = 0
comment_nums = response.css("a[href='#article-comment'] span::text").extract()[0]
match_re = re.match(".*?(\d+).*", comment_nums)
if match_re:
comment_nums = int(match_re.group(1))
else:
comment_nums = 0
content = response.css("div.entry").extract()[0]
tag_list = response.css("p.entry-meta-hide-on-mobile a::text").extract()
tag_list = [element for element in tag_list if not element.strip().endswith("评论")]
tags = ",".join(tag_list)
我们可以debug一下,看看是否正确

下一步,分析
parse获得文章列表页的url交给scrapy下载并进行解析
获取下一页的url并并交给scrapy进行下载,下载完成后交给parse

浙公网安备 33010602011771号