目录
1. 准备环境
安装virtualenv,使用virtualenv生成一个虚拟环境:
mkvirtualenv scrapy_env
使用pip安装scrapy:
pip install scrapy
在安装scrapy的时候有可能会出现以下异常:
error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": https://visualstudio.microsoft.com/downloads/
此时,需要手动安装Twisted-***.whl
才可以。Twisted-***.whl
可以从以下网站中获取:
https://www.lfd.uci.edu/~gohlke/pythonlibs/
注意,需要下载与本地Python版本以及计算机环境相符合的版本。
下载完之后,将其放到scrapy_env虚拟环境的根目录下(放在别的地方也无所谓),然后将cmd的路径切换到同一目录,执行以下命令进行安装:
pip install Twisted-18.9.0-cp37-cp37m-win_amd64.whl
安装完成后,再次安装scrapy
即可。
至此,环境准备完成。
2. 生成scrapy项目
将cmd路径切换到工作目录,然后执行以下命令,即可创建scrapy项目:
scrapy startproject [项目名]
然后我们将工作目录切换到刚创建的项目名下面,创建一个爬虫:
cd [项目名]
scrapy genspider [爬虫名] [域名]
(例如:scrapy genspider jobbole jobbole.com)
执行命令之后,scrapy会在spiders目录下自动生成一个[爬虫名].py
的文件,内容如下:
# -*- coding: utf-8 -*-
import scrapy
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['jobbole.com']
start_urls = ['http://www.jobbole.com/']
def parse(self, response):
pass
至此,我们就完成了爬虫项目的初始化工作。
3. 爬取数据
我们要爬取http://python.jobbole.com/all-posts/下的所有文章的标题、发布时间、类别。
3.1 创建Item
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html
from scrapy import Item, Field
from scrapy.loader import ItemLoader
from scrapy.loader.processors import MapCompose, TakeFirst
from item_filters.jobbole_filters import date_filter
class CommonItemLoader(ItemLoader):
"""
继承ItemLoader,为Item添加一个默认的output_processor
"""
default_output_processor = TakeFirst()
class JobboleItem(Item):
title = Field()
publish_date = Field(input_processor=MapCompose(date_filter))
category = Field()
url = Field()
3.2 自定义input_processor
在获取publish_date
的时候,需要用到自定义input_processor。我另外创建了一个文件,专门用于保存自定义input_processor函数。
import re
from w3lib.html import remove_tags
def date_filter(value: str) -> str:
"""
从tag中提取出YYYY/MM/DD的字符串
:param value: tag字符串
:return: YYYY/MM/DD
"""
value = remove_tags(value)
pattern = re.compile(r'\d{4}/\d{2}/\d{2}')
date: str = pattern.findall(value)[0]
return date
3.3 写爬虫
在jobbole.py文件中这样来写:
# -*- coding: utf-8 -*-
import scrapy
from scrapy import Request, Selector
from scrapy.http import HtmlResponse
from typing import List
from items import JobboleItem, CommonItemLoader
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['jobbole.com']
start_urls = ['http://python.jobbole.com/all-posts/']
def parse(self, response: HtmlResponse):
for i in range(1, 85):
next_url = f'{self.start_urls[0]}/page/{i}/'
yield Request(url=next_url, callback=self.parse_page)
def parse_page(self, response: HtmlResponse):
root_tag = '#archive .floated-thumb'
thumb_list: List[Selector] = response.css(root_tag)
thumb_count = len(thumb_list)
for i in range(1, thumb_count + 1):
current_thumb = f'{root_tag}:nth-child({i}) '
item_loader = CommonItemLoader(item=JobboleItem(), response=response)
item_loader.add_css('title', f'{current_thumb}.archive-title::text')
item_loader.add_css('url', f'{current_thumb}.archive-title::attr(href)')
item_loader.add_css('publish_date', f'{current_thumb}.post-meta >p:nth-child(1)')
item_loader.add_css('category', f'{current_thumb}a[rel="category tag"]::text')
item = item_loader.load_item()
yield item
4. 保存爬取结果
下面要把爬去的结果保存到MongoDB中。我们要添加依赖包:
pip install pymongo
4.1 pipelines
在pipelines.py文件中添加数据库保存逻辑:
from pymongo import MongoClient
import settings
class MongoPipeline:
def __init__(self):
host = settings.MONGODB_HOST
port = settings.MONGODB_PORT
db_name = settings.DB_NAME
client: MongoClient = MongoClient(host=host, port=port)
self.db = client[db_name]
def process_item(self, item, spider):
sheet_name = item.__class__.__name__
post = self.db[sheet_name]
post.insert(dict(item))
return item
4.2 在settings.py中添加配置
MONGODB_HOST = '192.168.3.12'
MONGODB_PORT = 27017
DB_NAME = 'article'
ITEM_PIPELINES = {
'scrapy_demo.pipelines.MongoPipeline': 300,
}
到此,我们就可以通过以下命令启动爬虫了:
scrapy crawl jobbole
5. 动态网页爬取
动态网页是指使用javascript来动态加载内容的页面。比如说,使用前端框架制作的网站,在最开始的时候是没有任何HTML内容的。而爬虫只能爬取HTML页面,所以如果以普通的方式爬取动态网页的时候,是没有办法爬到任何内容的。
此时,我们需要使用selenium来爬取动态网页。
首先,我们需要添加依赖包:
pip install selenium
然后,我们需要下载selenium的驱动。Chrome浏览器的下载地址如下:
http://chromedriver.chromium.org/downloads
5.1 改写spider代码
我们需要在jobbole.py
中添加selenium的初始化代码
# -*- coding: utf-8 -*-
import scrapy
from scrapy.xlib.pydispatch import dispatcher
from scrapy import Request, Selector, signals
from scrapy.http import HtmlResponse
from typing import List
from selenium import webdriver
from items import JobboleItem, CommonItemLoader
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['jobbole.com']
start_urls = ['http://python.jobbole.com/all-posts']
def __init__(self):
opt = webdriver.ChromeOptions()
opt.add_argument('--headless') # 添加该配置可以不显示浏览器
driver_path = 'D:\\app\\chromedriver_win32\\chromedriver.exe'
self.browser = webdriver.Chrome(executable_path=driver_path, options=opt)
super(JobboleSpider, self).__init__()
dispatcher.connect(self.spider_closed, signal=signals.spider_closed)
def spider_closed(self):
self.browser.quit()
def parse(self, response: HtmlResponse):
for i in range(1, 5):
next_url = f'{self.start_urls[0]}/page/{i}/'
yield Request(url=next_url, callback=self.parse_page)
print(f'..............{next_url}..............')
def parse_page(self, response: HtmlResponse):
root_tag = '#archive .floated-thumb'
thumb_list: List[Selector] = response.css(root_tag)
thumb_count = len(thumb_list)
for i in range(1, thumb_count + 1):
current_thumb = f'{root_tag}:nth-child({i}) '
item_loader = CommonItemLoader(item=JobboleItem(), response=response)
item_loader.add_css('title', f'{current_thumb}.archive-title::text')
item_loader.add_css('url', f'{current_thumb}.archive-title::attr(href)')
item_loader.add_value('root_url', response.url)
item_loader.add_css('publish_date', f'{current_thumb}.post-meta >p:nth-child(1)')
item_loader.add_css('category', f'{current_thumb}a[rel="category tag"]::text')
item = item_loader.load_item()
yield item
5.2 添加中间件
在middlewares.py
中添加中间件如下:
class JsPageMiddleware:
def process_request(self, request: Request, spider):
driver: WebDriver = spider.browser
driver.get(request.url)
return HtmlResponse(url=driver.current_url, body=driver.page_source, encoding='utf-8', request=request)
5.3 配置settings文件
最后把自定义的中间件配置到settings文件中
DOWNLOADER_MIDDLEWARES = {
'scrapy_demo.middlewares.JsPageMiddleware': 1
...
}
到此为止,我们就可以通过selenium来获取网页内容了。
6. 随机User-Agent
我们使用fake_useragent
插件。
pip install fake-useragent
6.1 添加中间件
class RandomUserAgentMiddleware:
def __init__(self, crawler: Crawler):
self.ua_type = crawler.settings.get('RANDOM_UA_TYPE', 'random')
self.ua: FakeUserAgent = UserAgent()
@classmethod
def from_crawler(cls, crawler: Crawler):
return cls(crawler)
def process_request(self, request: Request, spider):
user_agent = getattr(self.ua, self.ua_type)
request.headers.setdefault('User-Agent', user_agent)
6.2 配置settings
RANDOM_UA_TYPE = 'random'
DOWNLOADER_MIDDLEWARES = {
'scrapy_demo.middlewares.JsPageMiddleware': 1,
'scrapy_demo.middlewares.RandomUserAgentMiddleware': 1000
}
如此即可。