Scrapy 爬蟲入門

1. scrapy 項目目錄

scrapy是通過命令運行的爬蟲框架

scrapy startproject projectname  // 創建scrapy項目

通過命令創建scrapy項目後,會自動生成一個projectname的文件夾,其中包括一個spiders文件夾,以及items.py、pipelines.py、middlewares.py、settings.py等python文件。spiders文件夾用於放置不同的spider文件,items.py中用於定義需要獲取的數據容器,pipelines.py中定義的數據進一步加工與處理(如存放數據庫、下載圖片等),middlewares.py中定義自定義的中間件,一般用於自定義去重url邏輯、給request添加消息頭模擬瀏覽器、定義ip池等,settings.py爲爬蟲的設置文件。

2. 爬蟲items

import scrapy
class AppsSpiderItem(scrapy.Item):
    app_id = scrapy.Field()
    platform = scrapy.Field()
    ...

定義結構化數據信息的格式如下:

結構化數據名 = scrapy.Feild()

對於每一個需要提取的數據均需要定義對應的結構化數據名

3. 定義爬蟲

Spider類是Scrapy中與爬蟲相關的一個基類,所有爬蟲文件必須繼承該類(scrapy.Spider)

在爬蟲項目中可以通過genspider命令自動在spiders文件夾中創建一個爬蟲

scrapy genspider name domain
scrapy genspider test www.baidu.com
import scrapy

class TestSpider(scrapy.Spider):
    name = 'test'
    allowed_domains = ['www.baidu.com']
    start_urls = ['http://www.baidu.com/']

    def parse(self, response):
        pass

默認生成的spider只重寫了parse方法,運行時其會自動調用默認的start_requests(),從start_urls中獲取url發起請求,請求完成後會默認調用parse方法,在parse中編寫具體解析邏輯。

4. 通過post請求url

默認生成的request是通過get請求,當需要同過post請求時可以通過重寫start_requests方法,添加formdata

class TestSpider(scrapy.Spider):
    name = 'test'
    allowed_domains = ['www.baidu.com']
    start_urls = ['http://www.baidu.com/']

    def start_requests(self):
        url = self.start_urls[0]
        formdata = {'name':'mike'}
        yield scrapy.FormRequest(url=url, formdata=formdata, callback=self.parse)

    def parse(self, response):
        pass

自定義的request需要顯式地定義callback方法

5. 運行爬蟲

默認的運行方式是通過命令調用爬蟲

scrapy runspider test.py (爬蟲文件名)
scrapy crawl test(爬蟲名)

當想通過idea運行scrapy爬蟲時,可以通過py文件模擬cmdline

創建run.py文件,在其中定義

from scrapy import cmdline

name = 'google_play'
cmd = 'scrapy crawl {0}'.format(name) + ' --nolog'
cmdline.execute(cmd.split())

這種方式運行的爬蟲可以在idea中debug爬蟲,推薦使用

6. BeautifulSoup解析網頁

解析網頁一般通過正則表達式,xpath和BeautifulSoup解析,在此簡要介紹BeautifulSoup的常用方法

BeautifulSoup是將複雜的HTML文檔轉化爲一個複雜的樹型結構,每個節點都是一個python對象,所有對象可以歸納爲4種:

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment

1). Tag

Tag表示HTML中的一個個標籤,獲得一個節點後,可以通過 .tagname 獲得其子tag,如

soup.div
soup.a

這種方式當且僅當其這種子節點類型只有一個時使用,當其子節點同一類型有多個時,可硬通過 .contents獲得其所有子節點的集合。

soup.contents

.attrs 獲取標籤的所有屬性

print(soup.p.attrs)
#{'class': ['title'], 'name': 'dromouse'}

獲得某個屬性

print(soup.p['class']
#['title']
print(soup.p.get('class'))
#['title']

2). NavigableString

當想要獲取標籤的內容時可以通過 .string獲得

print soup.p.string
#The Dormouse's story

3). BeautifuSoup

BeautifulSoup對象表示一個文檔的全部內容

4). Comment (一般不用)

常用方法:

description = soup.find_all('div', attrs={'class': 'W4P4ne '})  // 獲得特定屬性的div標籤的集合
description[0].meta.attrs['content']                            // 獲得集合中第一個標籤的meta子標籤的‘content’屬性值

7. 模擬瀏覽器 自定義user-agent

一般情況下,我們需要不停更換用戶代理,降低被ban的風險。默認情況下,scrapy會給所有的request一個默認的user-agent。當需要自定義user-agent時,具體實現是通過request對象的headers屬性,以及自定義一個downloadmiddleware。

class AppSpiderDownloaderMiddleWare(object):

    def __init__(self, user_agent_list):
        self.user_agent = user_agent_list

    @classmethod
    def from_crawler(cls, crawler, *args, **kwargs):
        middleware = cls(crawler.settings.get('MY_USER_AGENT'))
        return middleware

    def process_request(self, request, spider):
        #隨機選擇一個user-agent
        request.headers['user-agent'] = random.choice(self.user_agent)

在上列中,我們自定義了一個下載中間件,這樣就可以保證每一個請求在交給下載器之前都會經過這裏,同時在創建中間件時,傳入了定義在settings中的user-agent集合。

MY_USER_AGENT = ["Mozilla/5.0+(Windows+NT+6.2;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/45.0.2454.101+Safari/537.36",
    "Mozilla/5.0+(Windows+NT+5.1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/28.0.1500.95+Safari/537.36+SE+2.X+MetaSr+1.0",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/50.0.2657.3+Safari/537.36",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/51.0.2704.106+Safari/537.36",
    "Mozilla/5.0+(Windows+NT+6.1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/47.0.2526.108+Safari/537.36+2345Explorer/7.1.0.12633",
    "Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_11_4)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/49.0.2623.110+Safari/537.36",
    "Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_9_5)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/42.0.2311.152+Safari/537.36",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/42.0.2311.152+Safari/537.36",
    "Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_10_2)+AppleWebKit/600.3.18+(KHTML,+like+Gecko)+Version/8.0.3+Safari/600.3.18",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/49.0.2623.22+Safari/537.36+SE+2.X+MetaSr+1.0",
    "Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_11_4)+AppleWebKit/601.5.17+(KHTML,+like+Gecko)+Version/9.1+Safari/601.5.17",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/48.0.2564.103+Safari/537.36",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/47.0.2526.80+Safari/537.36+Core/1.47.640.400+QQBrowser/9.4.8309.400",
    "Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_10_5)+AppleWebKit/600.8.9+(KHTML,+like+Gecko)+Version/8.0.8+Safari/600.8.9",
    "Mozilla/5.0+(Windows+NT+6.3;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/39.0.2171.99+Safari/537.36+2345Explorer/6.4.0.10356",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/45.0.2454.87+Safari/537.36+QQBrowser/9.2.5584.400",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/47.0.2526.111+Safari/537.36",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/33.0.1750.146+BIDUBrowser/6.x+Safari/537.36",
    "Mozilla/5.0+(Windows+NT+6.1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/39.0.2171.99+Safari/537.36+2345Explorer/6.5.0.11018",
    "Mozilla/5.0+(Windows+NT+6.2;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/42.0.2311.154+Safari/537.36+LBBROWSER"]

然後在settings.py文件中開啓Middleware配置

# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
   'apps_spider.middlewares.AppsSpiderDownloaderMiddleware': 543,
}

8. 設置ip池

在settings文件中定義ip集合

IPPOOL=[
	{"ipaddr":"61.129.70.131:8080"},
	{"ipaddr":"61.152.81.193:9100"},
	{"ipaddr":"120.204.85.29:3128"},
	{"ipaddr":"219.228.126.86:8123"},
	{"ipaddr":"61.152.81.193:9100"},
	{"ipaddr":"218.82.33.225:53853"},
	{"ipaddr":"223.167.190.17:42789"}
]

自定義中間件,在每次request前隨機添加ip地址

import random
from scrapy import signals
from myproxies.settings import IPPOOL
 
class ProxiesSpiderMiddleware(object):
 
      def __init__(self,ip=''):
          self.ip=ip
       
      def process_request(self, request, spider):
          thisip=random.choice(IPPOOL)
          print("this is ip:"+thisip["ipaddr"])
          request.meta["proxy"]="http://"+thisip["ipaddr"]

在settings文件中啓用這個中間件

# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
   'apps_spider.middlewares.ProxiesSpiderMiddleware': 543,
}

ip地址可以在免費ip,或在github中搜索proxy ip獲得

9. 保存數據到json

在pipelines中編寫處理items的邏輯

import codecs
import json
import urllib.request

class AppsSpiderPipeline(object):
    icon_path = "./icons"

    def __init__(self):
        self.file = codecs.open("./apps.json", "wb", encoding="utf-8")

    def process_item(self, item, spider):
        d = dict(item)
        icon_url = d.pop('icon_url')
        
        # 下載圖片
        local_path = self.icon_path + '/' + d['app_id'] + '.jpg'
        urllib.request.urlretrieve(icon_url, filename=local_path)
        
        # 持久化到json
        i = json.dumps(d, ensure_ascii=False)
        line = i + '\n'
        self.file.write(line)

        return item

    def close_spider(self, spider):
        self.file.close()

在settings中啓用pipelines

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'apps_spider.pipelines.AppsSpiderPipeline': 300,
}

10.解決ajax異步加載內容

某些網頁第一次請求僅獲取部分內容,當用戶瀏覽到頁面底端才通過js再次加載新的內容。對於這種頁面,只需在每次請求得到的response中定位到下次請求的網址,即可遞歸爬取到該頁面所有內容。以google play列表頁爲例:

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章