作为一位对联盟游戏的爱好者,学习爬虫的时候也以这款游戏作为对象。
这个项目使用的python版本:3.6.0,scrapy使用的版本:1.11。参照这篇博客即便是不会爬虫的小白也可以带你做出一个完整的scrapy项目。废话不多说现在就开始吧。
这里是github地址:GitHub
第一步:新建一个scrapy项目
scrapy startproject LOL
使用Windows PowerShell 执行这条命令,将会在当前的路径下新建一个LOL项目。出现一个LOL文件夹,LOL的目录结构如下:
LOL--LOL--__pycache__
| | --spiders--__pycache__
| | |--__init__.py
| | --__init__.py
| | --items.py
| | --middlewares.py
| | --pipelines.py
| | --setting.py
|--scrapy.cfg
清楚目录结构后简单的介绍一下文件的作用:
- spiders文件夹 就是放你写的小爬虫的。
items.py文件 Item 是保存爬取到的数据的容器;其使用方法和python字典类似, 并且提供了额外保护机制来避免拼写错误导致的未定义字段错误。类似在ORM中做的一样,您可以通过创建一个
scrapy.Item
类, 并且定义类型为scrapy.Field
的类属性来定义一个Item。middlewares.py文件 在我们爬虫的时候用到的中间件。
pipelines.py文件 管道。
- setting.py文件 spider的配置文件。
第二步:开始写我们的爬虫项目
1.Spiders
首先是我们的爬虫spiders文件。写一个继承scrapy.Spider的爬虫LOLSpider。
import scrapy
from LOL.items import LolItem
import re
class LOLSpider(scrapy.Spider):
def get_start_urls():
lol_summoner="Aatrox,Ahri,Akali,Alistar,Amumu,Anivia,Annie,Ashe,AurelionSol,Azir,Bard,Blitzcrank,Brand,Braum,Caitlyn,Camille,Cassiopeia,Chogath,Corki,Darius,Diana,Draven,DrMundo,Ekko,Elise,Evelynn,Ezreal,Fiddlesticks,Fiora,Fizz,Galio,Gangplank,Garen,Gnar,Gragas,Graves,Hecarim,Heimerdinger,Illaoi,Irelia,Ivern,Janna,JarvanIV,Jax,Jayce,Jhin,Jinx,Kalista,Karma,Karthus,Kassadin,Katarina,Kayle,Kayn,Kennen,Khazix,Kindred,Kled,KogMaw,Leblanc,LeeSin,Leona,Lissandra,Lucian,Lulu,Lux,Malphite,Malzahar,Maokai,MasterYi,MissFortune,MonkeyKing,Mordekaiser,Morgana,Nami,Nasus,Nautilus,Nidalee,Nocturne,Nunu,Olaf,Orianna,Ornn,Pantheon,Poppy,Quinn,Rakan,Rammus,RekSai,Renekton,Rengar,Riven,Rumble,Ryze,Sejuani,Shaco,Shen,Shyvana,Singed,SionSivir,Skarner,Sona,Soraka,Swain,Syndra,TahmKench,Taliya,Talon,Taric,Teemo,Thresh,Tristana,Trundle,Tryndamere,TwistedFate,Twitch,Udyr,Urgot,Varus,Vayne,Veigar,Velkoz,Vi,Viktor,Vladimir,Volibear,Warwick,Xayah,Xerath,XinZhao,Yasuo,Yorick,Zac,Zed,Ziggs,Zilean,Zoe,Zyra"
# lol_summoner = "Janna"
lol_summoner = lol_summoner.split(",")
start_urls = []
for summoner in lol_summoner:
url = "http://yz.lol.qq.com/zh_CN/story/champion/"
summoner = url + summoner + "/"
start_urls.append(summoner)
return start_urls
name = "lol_hero_story"
allowed_domains = ["http://yz.lol.qq.com/zh_CN/story/"]
start_urls = get_start_urls()
def parse(self, response):
print("-------------------我进入《英雄联盟宇宙》界面了--------------------")
lolItem = LolItem()
lolItem['name'] = response.xpath('//span[@class="alt__5Tm"]/text()').extract_first() #extract_first()返回第一个元素,无结果返回None
if response.xpath('//p[@id="CatchElement"]/p/text()').extract_first() == None:
# allStory = response.xpath('//p[@id="CatchElement"]/p/text()').extract() #extract()返回结果为selector list列表
summonerHtml = response.text
summonerHtml = summonerHtml.replace("\r","").replace("\n","").replace("<p>","").replace("</p>","").strip()
summoner_re = r'showFirstLetterEffect__2JK">(.+?)</div>'
bbb = re.compile(summoner_re , re.S)
aaa = bbb.findall(summonerHtml)
lolItem['story'] = aaa[0]
else:
lolItem['story'] = response.xpath('//p[@id="CatchElement"]/text()').extract()
yield lolItem
print("-------------------我离开《英雄联盟宇宙》界面了--------------------")
其中小爬虫的名字是我们自己定义的,我暂且定义 name = "lol_hero_story" 。allowed_domains = ["http://yz.lol.qq.com/zh_CN/story/"] 。start_urls 是存放将要爬虫的网址列表。我这里封装了一个方法来存放。接下接下来就是我们对结果的解析方法 parse 。我们通过xpath或则css选择器将数据解析得到自己需要的信息。再将信息存储再自己定义的Items中。
2.items
items将暂存我们得到的name和story。
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html
import scrapy
class LolItem(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field() #英雄名
story = scrapy.Field() #英雄故事
pass
需要什么字段在这里以name = scrapy.Field() 形式直接写就行。在items.py中我们可以定义多个需要存储的类似LolItems类。
3.pipelines.py
在这里pipelines.py用于连接和操作我们的数据库。
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
from twisted.enterprise import adbapi
import MySQLdb
import MySQLdb.cursors
import codecs
import json
from logging import log
import sys
# reload(sys)
# sys.setdefaultencoding('utf-8')
class LolPipeline(object):
@classmethod
def from_settings(cls,settings):
'''1、@classmethod声明一个类方法,而对于平常我们见到的则叫做实例方法。
2、类方法的第一个参数cls(class的缩写,指这个类本身),而实例方法的第一个参数是self,表示该类的一个实例
3、可以通过类来调用,就像C.f(),相当于java中的静态方法'''
dbparams=dict(
host=settings['MYSQL_HOST'],#读取settings中的配置
db=settings['MYSQL_DBNAME'],
user=settings['MYSQL_USER'],
passwd=settings['MYSQL_PASSWD'],
charset='utf8',#编码要加上,否则可能出现中文乱码问题
cursorclass=MySQLdb.cursors.DictCursor,
use_unicode=False,
)
dbpool=adbapi.ConnectionPool('MySQLdb',**dbparams)#**表示将字典扩展为关键字参数,相当于host=xxx,db=yyy....
return cls(dbpool)#相当于dbpool付给了这个类,self中可以得到
#得到连接池dbpool
def __init__(self,dbpool):
self.dbpool=dbpool
#process_item方法是pipeline默认调用的,进行数据库操作
def process_item(self, item, spider):
query=self.dbpool.runInteraction(self._conditional_update,item)#调用update的方法
query.addErrback(self._handle_error,item,spider)#调用异常处理方法
return item
#写入数据库中
def _conditional_insert(self,tx,item):
print("---------------这里是你想知道的tx---------------",tx)
sql="insert into testpictures(name,url) values(%s,%s)"
params=(item["name"],item["story"])
tx.execute(sql,params)
#错误处理方法
def _handle_error(self, failue, item, spider):
print (failue)
#查询数据库
def _conditional_select(self,tx,item):
print("---------------这里是测试_conditional_select方法----------------")
print("---------------这里是你想知道的tx---------------",tx)
sql="select count(*) from summoner"
tx.execute(sql)
reponse = tx.fetchall()
print(reponse)
#更新数据库
def _conditional_update(self,tx,item):
print("---------------这里是执行_conditional_update方法----------------")
try:
print("----------------这里是测试update-------------------")
sql="update summoner set story='%s' where first_name='%s' or last_name='%s'" %(item["story"],item["name"],item["name"])
n = tx.execute(sql)
print(n)
except expression as identifier:
raise identifier
文本中解释足够清楚,在这里我就不再赘述了。
4.setting
在setting中定义我们需要连接数据库的变量。
#Mysql数据库的配置信息
MYSQL_HOST = '111.230.227.159'#腾讯云公网IP
MYSQL_DBNAME = 'lol' #数据库名字,请修改
MYSQL_USER = 'root' #数据库账号,请修改
MYSQL_PASSWD = '******' #数据库密码,请修改
MYSQL_PORT = 3306 #数据库端口
CHARSET = "utf8"
将下面这段代码的注释去掉这样我们的pipeline才会发挥作用。这个文件根据大家的项目名称不同而不同。
ITEM_PIPELINES = {
'LOL.pipelines.LolPipeline': 300,
}
顺便也吧这个robots协议也给修改了吧。将True改为False。
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
5.middlewares
一个般的大型网站都有反爬虫机制,像撸啊撸这样的大型网站更不用多说了。所以我们应该封装一下自己,让自己的小爬虫像一个正常的浏览者。在这里我们就不在封装Request headers了,给大家介绍一个模拟软件。chromedriver,穿上这件圣衣就可以"为所欲为"了。
在这里就不再使用项目中的middlewares.py文件了,我们另建一个文件夹,放入我们自己的mymiddlewares.py。目录结构如下:
LOL--LOL--__pycache__
| | --spiders--__pycache__
| | |--__init__.py
| | --mymiddlewares--mymiddlewares.py
| | --__init__.py
| | --items.py
| | --middlewares.py
| | --pipelines.py
| | --setting.py
|--scrapy.cfg
文件内容如下:
from selenium import webdriver
from scrapy.http import HtmlResponse
import time
class zhongjianjian(object):
def process_request(self, request, spider):
print("************woshizhongjianjian**************************");
if spider.name == "lol_hero_story":
print ("Chromedriver is starting...")
chromedriver = "E:\chromedriver.exe"
driver = webdriver.Chrome(chromedriver) #指定使用的浏览器
driver.get(request.url)
time.sleep(1)
js = "var q=document.documentElement.scrollTop=10000"
driver.execute_script(js) #可执行js,模仿用户操作。此处为将页面拉至最底端。
time.sleep(3)
body = driver.page_source
print ("访问"+request.url)
return HtmlResponse(driver.current_url, body=body, encoding='utf-8', request=request)
else:
return None
webdriver中用很多我们需要的圣衣,感兴趣的小伙伴们可以自己去研究下。
第三步:启动我们的爬虫
启动爬虫的命令:scrapy crawl lol_hero_story
另外说一点:数据库中存储story的字段类型需要满足我们的需要,在这里我用的时text类型。
存储成功后就是这个样子了:
最后希望可以和喜爱爬虫的小伙伴一起相互交流、共同进步。文章中有哪些不足可以提出来一起交流学习呀。