作爲一位對聯盟遊戲的愛好者,學習爬蟲的時候也以這款遊戲作爲對象。
這個項目使用的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類型。
存儲成功後就是這個樣子了:
最後希望可以和喜愛爬蟲的小夥伴一起相互交流、共同進步。文章中有哪些不足可以提出來一起交流學習呀。