基础部分
1.创建Scrapy项目
命令行界面 **scrapy startproject 项目名称
**就会在当前目录下创建该爬虫项目
2.创建爬虫项目
通过**cd 项目名称
进入项目后
使用scrapy genspider 爬虫项目名称 网站域名(后期可改)
**命令创建该爬虫(一个项目可以创建多个爬虫)
3.运行爬虫
1.命令行界面**scrapy crawl 爬虫项目名称
**
2.在该项目下创建.py文件,这样每次运行爬虫只需要运行此脚本即可(文件内容)
from scrapy import cmdline
# 方式一:使用split()方法将字符串分隔成 方式二( (可选)--nolog:设置为无日志输出)
cmdline.execute("scrapy crawl 爬虫项目名称 --nolog".split())
# 方式二:注意execute的参数类型为一个列表
cmdline.execute(["scrapy","crawl","爬虫项目名称"])
4.爬虫各部分作用浅谈
项目
爬虫项目
# -*- coding: utf-8 -*-
import scrapy
from scrapy.selector import Selector
from MySpider.items import TianqiListItem # items中的类名
import time
import random
import json
import copy
import pymysql
import pymysql.cursors
class TianqiSpider(scrapy.Spider):
name = "TianqiSpider"
allowed_domains = ["tianqi.com"]
# 传入初始网页,多个网址可以使用列表加循环实现
def start_requests(self):
url = "https://lishi.tianqi.com/"
yield scrapy.Request(url, callback=self.parse1)
# 此部分需另外调用
def create_mysql(self):
"""在数据库中创建表"""
# 填写数据库用户名、数据库名、数据库用户密码、数据库url
connect = pymysql.connect(user='root', db='weather_db', passwd='123456',
host='127.0.0.1', charset="utf8", use_unicode=True)
# 获取游标
cursor = connect.cursor()
# 通过如果表存在则删除
# self.cursor.execute("DROP TABLE IF EXISTS data_table")
# 创建表
cursor.execute("CREATE TABLE weather_data (地名 CHAR(20),年月 CHAR(20),时间 CHAR(20),最高温度 int,最低温度 int,天气 CHAR(20),风向 CHAR(20),风速 CHAR(20),风向风速 CHAR(20),补充信息 CHAR(50),地方网址 CHAR(200),月份网址 CHAR(200),当天网址 CHAR(200))")
cursor.close()
# time.sleep(10)
def parse4(self, response):
"""调用本地文件中的内容数据"""
data_item = TianqiListItem()
dataJson = json.load(open("C:/Users/Yin/Desktop/Program/数据/data_table.json", "r", encoding='UTF-8'))
for each in dataJson:
data_item['url1'] = each["地方网址"]
data_item['data1'] = each["地名"]
data_item['url2'] = each["月份网址"]
data_item['data2'] = each["年月"]
# 随机休眠躲避反爬
time.sleep(random.uniform(1, 3))
yield scrapy.Request(url=each["url2"], meta={'data_item':copy.deepcopy(data_item)},
callback=self.parse3,
dont_filter=True, )
# 地区
def parse1(self, response):
data_item = TianqiListItem()
sel = Selector(response)
web1 = sel.xpath('//*[@class="tablebox"]/table/tbody/tr/td/ul/li')
for each2 in web1:
try:
url1 = "https://lishi.tianqi.com/" + each2.xpath('.//a/@href').extract()[0] # 地方网址
data1 = each2.xpath('.//a/text()').extract()[0] # 地名
data_item['url1'] = url1
data_item['data1'] = data1
# 随机休眠躲避反爬
time.sleep(random.uniform(1, 3))
#使用copy.deepcopy(data_item)传递数据,避免后期数据错乱[因为使用 Request 函数传递 item 时,使用的是浅复制(对象的字段值被复制时,字段引用的对象不会被复制)]
yield scrapy.Request(url=url1, meta={'data_item': copy.deepcopy(data_item)},
callback=self.parse2,
dont_filter=True, )
except:
pass
# 年份
def parse2(self, response):
data_item = response.meta['data_item']
sel = Selector(response)
web1 = sel.xpath('//*[@class="main clearfix"]/div[1]/div[11]/div/div/ul/li')
for each3 in web1:
url2 = each3.xpath('.//a/@href').extract()[0] # 月份网址
print(url2)
data2 = each3.xpath('.//a/text()').extract()[0] # 年月
data_item['url2'] = url2
data_item['data2'] = data2
# 随机休眠躲避反爬
time.sleep(random.uniform(2, 4))
yield scrapy.Request(url=url2, meta={'data_item': copy.deepcopy(data_item)},
callback=self.parse3,
dont_filter=True, )
# 月份
def parse3(self, response):
data_item = response.meta['data_item']
sel = Selector(response)
web1 = sel.xpath('//*[@class="main clearfix"]/div[1]/div[5]/div/div[2]/ul[2]/li')
# 查看过程中发现网页有新旧2种模板
if web1:
for each in web1:
url3 = each.xpath('.//div/a/@href').extract() # 当天网址
data3 = each.xpath('.//div/a/text()').extract() # 时间
data4 = each.xpath('.//div[2]/text()').extract() # 最高温度
data5 = each.xpath('.//div[3]/text()').extract() # 最低温度
data6 = each.xpath('.//div[4]/text()').extract() # 天气
try:
data_ = each.xpath('.//div[5]/text()').extract()[0].split()
data7 = data_[0] # 风向
data8 = data_[1] # 风速
except:
pass
try:
# 补充数据
data9 = sel.xpath('//div[5]/div[@class="linegraphbox"]/div[1]/text()').extract()[0].replace('\r','\t', '')
data_item['data9'] = data9
except:
data_item['data9'] = None
try:
data10 = sel.xpath('//div[5]/div[@class="linegraphbox"]/div[1]/text()').extract()[0].replace('\r','').replace('\n', '').replace('\t', '')
data_item['data10'] = data10
except:
data_item['data10'] = None
try:
data_item['url3'] = "https:" + url3[0]
except:
data_item['url3'] = None
try:
data_item['data3'] = data3[0]
except:
data_item['data3'] = None
try:
data_item['data4'] = data4[0]
except:
data_item['data4'] = None
try:
data_item['data5'] = data5[0]
except:
data_item['data5'] = None
try:
data_item['data6'] = data6[0]
except:
data_item['data6'] = None
try:
data_item['data7'] = data7
data_item['data8'] = data8
except:
data_item['data7'] = None
data_item['data8'] = None
# yield出去之后,这个Item就会进入到pipelines里面去
print(data_item)
yield data_item
else:
# 第二种网页模板
web2 = sel.xpath('//div[@class="tian_three"]/ul[@class="thrui"]/li')
try:
data10 = sel.xpath('//div[1]/div[@class="linegraphborder"]/div/div[@class="linegraphtitle"]/text()').extract()[0].replace('\r','').replace('\n', '').replace('\t', '')
except:
pass
for each in web2:
data3 = each.xpath('.//div[1]/text()').extract() # 时间
data4 = each.xpath('.//div[2]/text()').extract() # 最高温度
data5 = each.xpath('.//div[3]/text()').extract() # 最低温度
data6 = each.xpath('.//div[4]/text()').extract() # 天气
try:
data_ = each.xpath('.//div[5]/text()').extract()[0].split()
data7 = data_[0] # 风向
data8 = data_[1] # 风速
if data7 == data8:
data7,data8=None,None
data9 = data_[0]
except:
pass
try:
data_item['url3'] = None
except:
data_item['url3'] = None
try:
data3 = data3[0].split()
data_item['data3'] = data3[0]
except:
data_item['data3'] = None
try:
data_item['data4'] = data4[0].replace("℃", "")
except:
data_item['data4'] = None
try:
data_item['data5'] = data5[0].replace("℃", "")
except:
data_item['data5'] = None
try:
data_item['data6'] = data6[0]
except:
data_item['data6'] = None
try:
data_item['data7'] = data7
data_item['data8'] = data8
except:
data_item['data7'] = None
data_item['data8'] = None
try:
data_item['data9'] = data9
except:
data_item['data9'] = None
try:
data_item['data10'] = data10
except:
data_item['data10'] = None
yield data_item
items部分
# 天气网(新建一个类,类名在爬虫文件中用到)
class TianqiListItem(scrapy.Item):
data1= scrapy.Field() # 地名
data2= scrapy.Field() # 年月
data3= scrapy.Field() # 时间
data4= scrapy.Field() # 最高温度
data5= scrapy.Field() # 最低温度
data6= scrapy.Field() # 天气
data7= scrapy.Field() # 风向
data8= scrapy.Field() # 风速
data9= scrapy.Field() # 风向风速
data10= scrapy.Field()# 补充信息
data11= scrapy.Field()
url1= scrapy.Field() # 地方网址
url2 = scrapy.Field() # 月份网址
url3 = scrapy.Field() # 当天网址
middlware部分
随机生成更换user-agent的类
from fake_useragent import UserAgent
class RandomUserAgentMiddlware(object):
"""随机更换user-agent
"""
def __init__(self,crawler):
super(RandomUserAgentMiddlware,self).__init__()
self.ua = UserAgent() # 函数实例
# @classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
@classmethod
def from_crawler(cls,crawler):
return cls(crawler)
def process_request(self,request,spider):
request.headers.setdefault("User-Agent",self.ua.random,) # 调用函数,生成随机用户代理
pipelines部分
数据存入Mysql:
import pymysql
import pymysql.cursors
class MyspiderMysqlPipeline(object):
"""
提交数据到mysql
"""
def __init__(self):
# 分别填写 数据库用户名、数据库用户密码、数据库名、数据库地址(127.0.0.1表示本地数据库)、指定编码格式(防止乱码)、是否指定字符的编码/解码格式
self.connect = pymysql.connect(user='root', passwd='123456',db='weather_db',
host='127.0.0.1',charset="utf8", use_unicode=True)
# 获取游标(分别后面使用游标操作数据库)
self.cursor = self.connect.cursor()
try:
# # 删除原有的表
# self.cursor.execute("DROP TABLE IF EXISTS data_table")
#使用SQL语句创建表
self.cursor.execute("CREATE TABLE data_table(地名 CHAR(20),年月 CHAR(20),时间 CHAR(20),最高温度 int,最低温度 int,天气 CHAR(20),风向 CHAR(20),风速 CHAR(20),风向风速 CHAR(20),补充信息 CHAR(50),地方网址 CHAR(200),月份网址 CHAR(200),当天网址 CHAR(200))")
except:
print("表已存在")
#关闭游标
# self.cursor.close()
def process_item(self, item, spider):
"""数据入库"""
try:
#使用if语句对传入的数据判断是来自哪个爬虫项目,已进行区分存储
if spider.name == "TianqiSpider":
# 设置插入格式
sql_text = 'insert into data_table(地名,年月,时间,最高温度,最低温度,天气,风向,风速,风向风速,补充信息,地方网址,月份网址,当天网址)values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
# 执行SQL语句,传入数据(item为 items文件中所设置的数据格式)
self.cursor.execute(sql_text, (item["data1"],item["data2"],item["data3"],item["data4"],item["data5"],item["data6"],item["data7"],item["data8"],item["data9"],item["data10"],item["url1"],item["url2"],item["url3"]))
# 提交,不进行提交无法保存到数据库
self.connect.commit()
return item
except:
pass
数据储存为Json格式
from scrapy.exporters import JsonItemExporter
class MyspiderJsonPipeline(object):
"""
数据以Json格式保存
"""
def __init__(self):
self.file = open("Weather_data.json","wb") # 生成json文件,并设置操作模式
self.exporter = JsonItemExporter(self.file,encoding="utf-8",ensure_ascii=False)
self.exporter.start_exporting()
def close_spider(self,spider):
self.exporter.finish_exporting()
self.file.close()
def process_item(self,item,spider):
self.exporter.export_item(item)
return item
settings部分
# 是否遵守爬虫规则
ROBOTSTXT_OBEY = False
# 是否激活cookie,即是否使用scrapy自带的cookie
COOKIES_ENABLED = False
# 用于限制爬行速度
DOWNLOAD_DELAY = 1 # 1000 ms of delay
# 设置线程数量(默认16)
CONCURRENT_REQUESTS = 16
# 添加中间件
DOWNLOADER_MIDDLEWARES = {
'MySpider.middlewares.RandomUserAgentMiddlware' : 400,
}
# 管道文件即文件保存
ITEM_PIPELINES = {
'MySpider.pipelines.MyspiderMysqlPipeline': 300,
'MySpider.pipelines.MyspiderJsonPipeline': 301,
}