python scrapy 抓取腳本之家文章(scrapy 入門使用簡介)


  老早之前就聽說過python的scrapy。這是一個分布式爬蟲的框架,可以讓你輕松寫出高性能的分布式異步爬蟲。使用框架的最大好處當然就是不同重復造輪子了,因為有很多東西框架當中都有了,直接拿過來使用就可以了。scrapy 就是一個很棒的框架。最近在看崔慶才老師的博客http://cuiqingcai.com/ 的時候,發現了幾個寫的非常好的scrapy教程(http://cuiqingcai.com/4380.html,http://cuiqingcai.com/3952.html等,還有很多,大家可以自己去看),我看了半個小時就把以前看的scrapy基礎回憶起來了,而且又學到了很多新的東西,所以就手癢癢用scrapy寫了一個自己的爬蟲,記錄在這里。

  腳本之家(http://www.jb51.net/)是我寫代碼查某個函數怎么怎么用(有時候忘了)經常去的地方,雖然站點界面像shi一樣,又有許多雜七雜八的廣告,不過說句公道話,還有有不少干貨的,大部分也都附有源代碼。今天我要爬取的就是腳本之家的文章。

  在開始之前,先簡單介紹一下scrapy的常用命令吧。我不打算講的非常詳細,要想詳細了解,可以參考文檔(http://wiki.jikexueyuan.com/project/scrapy/)或者上面我說的幾篇博客。scrapy 創建一個 項目 使用的命令是: scrapy crawl project_name (project_name是你的項目名稱) 這個命令需要在cmd(windows)或者shell(linux)下鍵入,這就會在當前目錄下創建名稱為 project_name 的項目。然后 cd 到這個項目,輸入命令 genspider your_spider_name 來快速創建一個爬蟲,your_spider_name 為你的爬蟲的名字,注意這個名字必須是唯一的,這個命令在 project_name/project_name/spiders/目錄下 生成了一個 your_spider_name.py 文件,你的爬蟲就寫到這里啦。

  常見的幾個文件作用如下:

  spiders 文件夾用來存放你寫的爬蟲的腳本

  items.py 用來定義你想要抓取的數據字段

  middlewares.py 用來給scrapy增加一些額外的自定義的功能(比如后面要講的設置代理等等)

  piplines.py 用來定義抓取數據的儲存方式

  settings.py 是用來設置爬蟲參數的文件

  打開 爬蟲文件,一般是已經給你 寫好了一個 類,類似這樣:

class JbzjSpiderSpider(scrapy.Spider):
    name = "jbzj_spider"
    allowed_domains = ["www.jb51.net"]
    base_url = "http://www.jb51.net"
    start_urls = []
    
    def parse(self, response):
        pass

   其中 name 是爬蟲的名字(唯一),allowed_domains 為允許抓取的域名,start_urls 為 起始抓取的列表,如果沒有特別指定抓取的url,就從start_urls列表中的地址抓取,類必須繼承自 scrapy.Spider,這是所有爬蟲都必須繼承的一個類,parse 是 scrapy.Spider 的一個方法,我們有的時候需要將他覆寫(override),這個方法是默認的回調函數(callback),如果沒有指定函數的回調函數的話,就會默認調用 parse函數。response 是解析 url 得到的相應,里面包含響應頭,響應網頁源碼,url等等,比如 response.body 得到 網頁源碼,response.url 得到響應的url。對於解析網頁,scrapy默認使用的方法是xpath 解析。比如可以直接使用 response.xpath("//a[@id="id1"]/@href").extract()得到id = id1的a標簽的href屬性(xpath的用法大家自行搜索,入門很快),使用 extract 方法返回的是一個列表。當然,除了使用xpath,你可以得到 response.body 之后,再使用你習慣解析html的方法(正則,css,bs4等等)。最后,一般我們需要 yield 一個 scrapy.Request 即相當於返回一個請求,這個請求可以設置很多參數,比較重要的有 headers(頭部信息),callback(回調函數),meta(傳遞額外信息,默認傳遞的只是response)。比如我們寫 yield scrapy.Request(url,callback=self.parse_url),就是設定回調函數為 parse_url 函數,我們將 response 傳遞給parse_url 進行進一步解析。

  還有一點就是,在這里我們將爬取到的數據存入數據庫,python 連接數據庫一般有 MySQLdb 和 pymysql 兩個驅動可以選擇,我一般使用的是前者,但是比較坑爹的是

MySQLdb 好像只支持 32 位系統,反正我用64位的python 裝了好多次都沒成功。所以建議使用 pip install pymysql 來安裝,pymysql 使用純python寫的驅動,用法和 mysqldb差不多。我們需要在piplines.py 中 寫插入數據庫的操作,代碼大概像下面這樣:

 1 import pymysql
 2 
 3 class JbzjPipeline(object):
 4     def process_item(self, item, spider):
 5         url = item['article_url'] # 文章url
 6         title = item['article_title'] # 文章標題
 7         content = item['article_content'] # 內容
 8         # 建立數據庫連接
 9         conn = pymysql.connect(host = 'localhost',user = 'root',passwd = 'passwd',db = 'your_db',charset = 'utf8')
10         cursor = conn.cursor()
11         sql = "insert into jbzj VALUES(NULL,%s,%s,%s)"
12         cursor.execute(sql,(url,title,content)) # 執行sql語句
13         cursor.close()
14         conn.commit() # 提交數據庫
15         print(u"成功插入一條數據!")
16         conn.close() # 關閉連接

我們需要覆寫 JbzjPipeline 類的 process_item 方法,這個方法在 yield item 之后會自動調用,需要傳入兩個參數,一個是item(數據字段),一個是spider(哪個爬蟲),我們在這個函數下寫插入數據進入數據庫的操作就可以了。

對了還有 items.py 文件,大概長下面這樣:

class JbzjItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    article_url = scrapy.Field() # 文章鏈接
    article_title = scrapy.Field() # 文章標題
    article_content = scrapy.Field() # 文章內容

 我們采用 name = scrapy.Field()這樣的形式來定義我們需要的字段,類需要繼承自 scrapy.Item,在這里我就簡單定義了文章鏈接、內容和標題三個字段。

總結一下:scrapy 抓取的基本步驟大概就是:從start_urls 中的url開始抓取,默認調用 start_requests ,然后將響應的請求傳給 parse方法,parse方法再傳遞給它的回調函數,以此類推,直到最后一層 yield item,然后 piplines.py 開始處理數據的儲存,當然我說的很簡單,實際處理的過程比這個還要復雜一點,scrapy默認就是開啟多線程的,整個過程不是順序執行,如果想要徹底弄明白scrapy運行的機制,可以去找官方文檔。

    最后 給出實際的代碼:

jbzj_spider.py

# -*- coding: utf-8 -*-
'''
scrapy 腳本之家爬蟲實例:http://www.jb51.net/article/54323.htm
'''
import re

import scrapy
from ..items import JbzjItem
from scrapy.selector import Selector


class JbzjSpiderSpider(scrapy.Spider):
    name = "jbzj_spider"
    allowed_domains = ["www.jb51.net"]
    base_url = "http://www.jb51.net"
    start_urls = [
        'http://www.jb51.net/article/109909.htm',
        'http://www.jb51.net/article/110219.htm'
                  ]

    # def start_requests(self):
    #     yield scrapy.Request(self.start_urls[0],callback=self.parse)

    def parse(self, response):
        html = response.body # 網頁源碼
        urls_list = re.findall(re.compile(r'<a href="(/article/\d+\.htm)".+?</a>'),html)
        full_urls_list = [self.base_url + url for url in urls_list] # 完整列表
        for url in full_urls_list:
            yield scrapy.Request(url,callback=self.parse_url)

    def parse_url(self,response):
        item = JbzjItem() # 實例化一個item
        selector = Selector(response) # 構造一個選擇器
        title = selector.xpath("//div[@class='title']/h1/text()").extract()[0] # 標題
        content = selector.xpath("//div[@id='content']//text()").extract() # 內容
        item['article_url'] = response.url
        item['article_title'] = title
        item['article_content'] = "".join(content)
        yield item
        html = response.body # 網頁源碼
        urls_list = re.findall(re.compile(r'<a href="(/article/\d+\.htm)".+?</a>'),html)
        full_urls_list = [self.base_url + url for url in urls_list] # 完整列表
        for url in full_urls_list:
            yield scrapy.Request(url,callback=self.parse_url2)


    def parse_url2(self,response):
        item = JbzjItem() # 實例化一個item
        selector = Selector(response) # 構造一個選擇器
        title = selector.xpath("//div[@class='title']/h1/text()").extract()[0] # 標題
        content = selector.xpath("//div[@id='content']//text()").extract() # 內容
        item['article_url'] = response.url
        item['article_title'] = title
        item['article_content'] = "".join(content)
        yield item

對了,為了使用piplines.py,我們需要將 settings.py 中的 ITEM_PIPELINES 一項注釋去掉,不然無法使用 piplines。

以上就是 本文的基本內容,后續有時間還會更新 scrapy的其他方面的內容。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM