scrapy 基礎學習

#scrapy 的優勢
1 request和beautifulsoup是庫,scrapy是框架,不是一個層次的
2 scrapy框架中可以加入request和beautifulsoup,可以基層很多第三方庫
3 scrapy基於twisted,實際上是一個異步i/o的框架,所以性能是最大的優勢
4 scrapy方便擴展,提供了很多內置功能
5 scrapy內置的css和xpath selector 非常方便,beautifulsoup最大的缺點就是慢。

#網頁分類

1 靜態網頁
事先在服務器端生成好的
2 動態網頁
根據傳遞的參數不同,發送請求獲取瀏覽器
3 webservice(restapi)

#爬蟲能做什麼
1 搜索引擎—百度 谷歌 垂直搜索引擎
2 推薦引擎–今日頭條 根據瀏覽記錄來爬取相關內容推薦給用戶
3 機器學習的數據樣本
4 數據分析,輿情分析

#正則表達式
!正則表達式是反向匹配,從後往前
!正則是默認貪婪模式,貪婪模式,就是在整個表達式匹配成功的前提下,儘可能多的匹配
!正則表達式會提取括號內的內容
1 "^" :定義開頭字符,以符號後面的字符開頭,"^b"代表以b字母開頭
"$":定義結尾字符,以符號前面的字符結尾
2 ".": 任意字符,中英文下劃線都可以
3 “*”:它前面的字符可以出現多次
4 “?”:非貪婪模式,非貪婪模式,就是在整個表達式匹配成功的前提下,儘可能少的匹配
5 “+” :符號前面的內容至少出現一次,
6 {2} 前面的字符出現的次數
{2,5} 出現最小2次最多5次
{2,}出現2次或者更多
7 “|”:或,要在括號裏使用
8“[abcd]”:中括號裏面的字符滿足一個即可,
可以寫區間[0-9] a-z [A-Za-z0-9_]
中括號裏面寫:^1 表示非1
9 \s 空格
\S 只要不是空格都可以
\w [A-Za-z0-9_]
\W 和\w相反
\d 數字
10 [\u4E00-\u9FA5] 漢字

深度優先和廣度優先
深度優先,遞歸實現,如果遞歸太深或者遞歸沒有跳出會發生棧溢出
廣度優先,隊列實現

爬蟲去重策略

1 把訪問過的url保存到數據庫中,但是速度慢
2 把訪問過的url保存到set中,也就是內存中,只需要o(1)的代價就可以查詢到
url,但是內存佔用大
3.url經過md5等方法哈希後保存到set中,可以將字符縮減到固定長度,比如128bit,就是16byte
4.用bitmap方法,將訪問過的url通過hash函數映射到某一位,申請一個8個bit,也就是一個byte,將訪問過的hash函數映射到某一個位置上
5 bloofilter方法對bitmap進行改進,多重hash函數降低衝突

#xpath 中的css語法
css選擇器

選擇器

css

用scrapy創建項目
1.解析文章
2.獲取列表和下一頁,用Request,傳url,來調用解析文章的方法
3. item
在item.py中創建一個item實例,然後在jobbole爬取文章後實例化這個item並填充數據,調用yeild 實例 就會把item傳到pipelins中
下載圖片並保存到本地
創建一個images文件夾
要把setting.py中的

	    ITEM_PIPELINES = {
	    	    'ArticleSprider.pipelines.ArticlespriderPipeline': 300,
	    	}
	
	打開,改成:
	
	    ITEM_PIPELINES = {
	        'ArticleSprider.pipelines.ArticlespriderPipeline': 300,
	        'scrapy.pipelines.images.ImagesPipeline': 1  ,# 後面的數字越小,越優先執行
	    }
	    IMAGES_URLS_FIELD = "front_img_url"  # 這樣scrapy會去找到這個front_img_url 然後下載圖片
	    project_dir = os.path.abspath(os.path.dirname(__file__))  # 當前目錄的路徑
	    IMAGES_STORE = os.path.join(project_dir,'images')   # 配置圖片保存路徑
	記得import os ,配置以後會把front_img_url當成一個數組,所以在填充數據的時候記得以數組填充。這裏還需要安裝一個東西,在cmd進入虛擬環境`pip install pillow`  用豆瓣雲會快一點

獲取圖片的路徑
重寫pipelines文件中處理圖片的方法,
導入scrapy.pipelines.images包,獲取path並填充到item在返回
from scrapy.pipelines.images import ImagesPipeline
class ArticleImagePipeline(ImagesPipeline):
def item_completed(self, results, item, info):
for ok, value in results:
image_file_path = value[“path”]
item[“front_img_path”] = image_file_path
return item
更改settings文件:
ITEM_PIPELINES = {
‘ArticleSprider.pipelines.ArticlespriderPipeline’: 300,
# ‘scrapy.pipelines.images.ImagesPipeline’: 1 ,# 後面的數字越小,越優先執行
‘ArticleSprider.pipelines.ArticleImagePipeline’: 1,
}
把圖片的url經過md5計算後的結果
創建一個保存常用方法的目錄,utils,在utils下新建一個common文件,在common中編寫一個計算MD5的方法,接着在解析文章的方法中導入這個方法,再調用方法填充到item實例
把item寫成json文件
在pipelines中,寫一個方法打開文件,然後把傳過來的item轉成dict,再轉成json
然後用self.file.write(lines)寫入文件中,接着關閉self.file.close()
import codecs # 可以避免很多編碼問題
import json
class JsonWithEncodingPipeline(object):
def init(self):
self.file = codecs.open(‘article.json’, ‘w’, encoding=‘utf-8’)
def process_item(self, item, spider):
lines = json.dumps(dict(item), ensure_ascii=False) + ‘\n’
self.file.write(lines)
return item
def spider_closed(self,spider):
self.file.close()
更改setting:
ITEM_PIPELINES = {
‘ArticleSprider.pipelines.JsonWithEncodingPipeline’: 2,
# ‘scrapy.pipelines.images.ImagesPipeline’: 1 ,# 後面的數字越小,越優先執行
‘ArticleSprider.pipelines.ArticleImagePipeline’: 1,
}

4.將item保存到數據庫
方法(一)
1 進入虛擬環境,安裝 mysqlclient: pip install mysqlclient
2.定義pipelines

class MysqlPipeline(object):
    def __init__(self):  #連接數據庫
        self.conn = MySQLdb.connect('host', 'user', 'password', 'dbname', charset='utf8', use_unicode=True)
        self.cursor = self.conn.cursor()

    def process_item(self,item,sprider):  #執行sql
        insert_sql = "insert into jobbole_article(title,url,create_date,fav_nums) VALUES (%s,%s,%s,%s)"
        self.cursor.execute(insert_sql, (item['title'], item['url'], item['create_date'], item['fav_nums']))
        self.conn.commit()

3.在setting中配置pipeline: ‘BlizzaedSpider.pipelines.MysqlPipeline’: 1,

方法(二) 爬取數據多了之後插入速度會跟不上爬取速度,就會發生堵塞,所以考慮第二種方法

1 在setting文件最後加上 配置

MYSQL_HOST = "192.168.0.106"
MYSQL_DBNAMQ = "jobbole_article"
MYSQL_USER = "root"
MYSQL_PASSWORD = "root"

2 創建pipeline


from twisted.enterprise import adbapi

.....

class MysqlTwistedPipeline(object):
    def __init__(self,dbpool):
        self.dbpool = dbpool

    @classmethod
    #  通過這個註解定義這個函數,這個函數實際上就是一個setting,可以來讀取setting的
    def from_settings(cls, settings):
        #  這個方法的名稱是固定的,在初始化的時候就被scrapy調用
        dbparms = dict(
        host = settings["MYSQL_HOST"],
        db = settings["MYSQL_DBNAME"],
        user = settings["MYSQL_USER"],
        password = settings["MYSQL_PASSWORD"],
        charset = 'utf8',
        cursorclass = MySQLdb.cursors.DictCursor,
        use_unicode = True,
        )


        dbpool = adbapi.ConnectionPool("MySQLdb",**dbparms)

        return cls(dbpool)

    def process_item(self,item,spider):
        #  使用twisted將mysql插入變成異步執行
        query = self.dbpool.runInteraction(self.do_insert, item)
        query.addErrback(self.handle_error, item, spider) #處理異常

    def handle_error(self,failure, item, spider):
        #  處理異步插入的異常
        print(failure)

    def do_insert(self, cursor, item):
        #執行具體的插入
        insert_sql = "insert into jobbole_article(title,url,create_date,fav_nums) VALUES (%s,%s,%s,%s)"
        self.execute(insert_sql, (item['title'], item['url'], item['create_date'], item['fav_nums']))
        #會自動commit

itemLoder
目的:爲了使代碼更便於維護,提高可配置性,可以把規則存到數據庫,需要的時候再到數據庫查詢,做一個匹配映射,還可以簡省代碼
items:

from scrapy.loader import ItemLoader


class NgaItemLoder(ItemLoader):
    default_output_processor = TakeFirst()

def update_time(value):
    if value:
        time = value.replace('上傳於', '')
        date = time.strip()
    else:
        date = datetime.datetime.now().date()
    return date


def get_nums(value):
    if value:
        nums = int(value[0])
    else:
        nums = 0
    return nums


class NgaspiderItem(scrapy.Item):
    url = scrapy.Field()
    url_id = scrapy.Field()
    front_img_url = scrapy.Field()
    front_img_url_path = scrapy.Field()
    title = scrapy.Field()
    update_time = scrapy.Field(
        input_processor=MapCompose(update_time)
    )
    author = scrapy.Field()
    playnum = scrapy.Field(

    )
    comment_count = scrapy.Field(
        input_processor=MapCompose(get_nums)
    )
    like_count = scrapy.Field(
        input_processor=MapCompose(get_nums)
    )

使用:

  def parse_details(self, response):
 		duowan = NgaspiderItem()
        itemloder = NgaItemLoder(item = NgaspiderItem(), response = response)
        itemloder.add_value("url", response.url)
        itemloder.add_value("url_id", get_md5(response.url))
        image_url_list = []
        image_url_list.append(response.meta.get('front_img_url', ''))
        itemloder.add_value("front_img_url", [image_url_list])
        itemloder.add_css("title", ".play-title::text")
        itemloder.add_xpath("update_time", '//*[@id="wrap"]/div/div[3]/div[1]/div[3]/div[1]/div[1]/span/text()')
        itemloder.add_xpath('author', '//*[@id="wrap"]/div/div[3]/div[1]/div[3]/div[1]/div[1]/h3/a/text()')
        itemloder.add_css('playnum', '#video_play_num::attr(data-play-num)')
        itemloder.add_xpath('comment_count', '//*[@id="first_comment_count"]/text()')
        itemloder.add_xpath('like_count', '//*[@id="wrap"]/div/div[3]/div[1]/div[3]/div[1]/div[3]/div[2]/a[3]/text()')
        duowan = itemloder.load_item()

        yield duowan


收集所有404url以及404頁面數(數據收集器的使用)

 # 收集所有404url以及404頁面數
    handle_httpstatus_list = [404]

    def __init__(self):
        self.fail_urls = []


    def parse(self, response):
        if response.status == 404:
            self.fail_urls.append(response.url)
            self.crawler.stats.inc_value('failed_url')
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章