当我们完成了环境配置之后,所要做的就是使用 scrapy 爬取相关数据了。
接下来,我们以伯乐在线网站为例,进行实际操作。
目的:抓取 http://blog.jobbole.com/all-posts/ 网址下的全部文章信息,包括“标题”、“创建时间”、“封面图”、“点赞数”、“收藏数”、“评论数”以及“文章内容”。
新建项目
首先,我们要完成scrapy项目的新建,在cmd下运行如下指令:
workon article(此为虚拟环境名称)
scrapy startproject articlespider(此为 scrapy 项目名称)
如图所示:
然后,根据提示,再运行指令:
cd test1
scrapy genspider bole(文件名称) http://blog.jobbole.com/all-posts/(网址)
接下来,我们用 pycharm 打开这个项目,并配置解释器即可,具体方法如下:
- 打开 pycharm,open project,然后在主目录下新建一个
main.py
。
- 在 File - Setting 中,输入
project
,选择project interpreter
。然后点击add local
,找到虚拟环境目录下article - Scripts - python.exe
,设置为解释器。
模块分析
仔细观察我们新建的 scrapy 项目,发现其包含几个.py
文件,这些文件直接的关系为:
main 文件
用于执行爬虫程序,也可以进行相关debug。
from scrapy.cmdline import execute
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# 将文件路径进行添加
execute(["scrapy", "crawl", "jobbole"])
jobbole 文件
爬虫函数主程序,具体解释见代码:
# -*- coding: utf-8 -*-
import scrapy
import re
import datetime
from ArticleSpider.items import JobBoleArticleItem
from scrapy.http import Request
from urllib import parse
from scrapy.loader import ItemLoader
from ArticleSpider.items import ArticleItemLoader
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
start_urls = ['http://python.jobbole.com/all-posts/']
def parse(self, response):
## 提取全部文章url并交给scrapy进行下载后解析
## 具体提取方法可以采用 xpath 和 css
## 使用谷歌浏览器可以直接 copy xpath
## 火狐浏览器的 xpath 为动态加载后的,无法提取到相关元素,不推荐。
post_nodes = response.xpath("//div[@id='archive']/div[@class='post floated-thumb']/div[@class='post-thumb']/a")
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()
# 文章网址,用于传递为参数,进入文章页面查找相关信息
yield Request(url = parse.urljoin(response.url, post_url), meta = {"front_image_url":image_url}, callback = self.parse_detail)
## 提取下一页url并交给scrapy进行下载
next_url = response.xpath("//div[@class='navigation margin-20']/a[@class='next page-numbers']/@href").extract_first()
# 提取“下一页”网址,用于遍历全部网页
if next_url:
yield Request(url = parse.urljoin(response.url, next_url), callback = self.parse)
def parse_detail(self, response):
# 通过item loader加载item
item_loader = ArticleItemLoader(item=JobBoleArticleItem(), response=response)
item_loader.add_css("title", ".entry-header h1::text")
item_loader.add_css("create_date", "p.entry-meta-hide-on-mobile::text")
item_loader.add_value("url", response.url)
front_image_url = response.meta.get("front_image_url", "")
item_loader.add_value("front_image_url", [front_image_url])
item_loader.add_css("like_nums", ".vote-post-up h10::text")
item_loader.add_css("collect_nums", ".bookmark-btn::text")
item_loader.add_css("comment_nums", "a[href='#article-comment'] span::text")
item_loader.add_css("content", "div.entry")
article_items = item_loader.load_item()
yield article_items
items 文件
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html
import datetime
import scrapy
import re
from scrapy.loader.processors import MapCompose, TakeFirst
from scrapy.loader import ItemLoader
def date_convert(value):
# 时间转换函数,将create_time转换为正确格式,如无正确时间,则使用当前时间。
try:
create_date = datetime.datetime.strptime(value, "%Y/%m/%d").date()
except Exception as e:
create_date = datetime.datetime.now().date()
return create_date
def get_nums(value):
# 提取“评论”和“收藏”数字
match_re = re.search(r"\d+", value)
if match_re:
nums = int(match_re.group(0))
else:
nums = 0
return nums
def return_value(value):
return value
class ArticleItemLoader(ItemLoader):
default_output_processor = TakeFirst()
class JobBoleArticleItem(scrapy.Item):
title = scrapy.Field()
create_date = scrapy.Field(
input_processor = MapCompose(date_convert)
)
url = scrapy.Field()
front_image_url = scrapy.Field(
output_processor = MapCompose(return_value)
)
like_nums = scrapy.Field()
collect_nums = scrapy.Field(
input_processor=MapCompose(get_nums)
)
comment_nums = scrapy.Field(
input_processor=MapCompose(get_nums)
)
content = scrapy.Field()
pipelines 文件
import codecs
import json
class JsonWithEncodingPipeline(object):
# 自定义存储json文件
def __init__(self):
self.file = open("article.json", "w", encoding='utf-8')
def process_item(self, item, spider):
lines = json.dumps(dict(item), ensure_ascii=False) + '\n'
self.file.write(lines)
return item
setting 文件
setting 中需要注意几点:
ROBOTSTXT_OBEY = False
:这是将网站正常访问检查关闭,以便爬虫能够运行。- 在 items_pipelines 处需要进行如下设置,其中后面的数字表示执行顺序,数字越小,执行顺序越靠前:
ITEM_PIPELINES = {
'ArticleSpider.pipelines.JsonExporterPipeline': 2,
# 用于保存json文件
'scrapy.pipelines.images.ImagesPipeline': 1,
# 用于下载图片
}
IMAGES_URLS_FIELD = "front_image_url"
project_dir = os.path.abspath(os.path.dirname(__file__))
IMAGES_STORE = os.path.join(project_dir, 'images')
# 设置下载图片存放路径