Python scrapy爬蟲爬取伯樂在線所有文章,並寫入數據庫

伯樂在線爬蟲項目目的及項目準備:python

1.使用scrapy建立項目sql

2.建立爬蟲,bole 域名 jobbole.com數據庫

3.Start_urls = [‘http://blog.jobbole.com/all-posts/’]api

4.爬取全部頁數的文章dom

5.文章列表頁須要數據異步

a) 縮略圖的地址scrapy

b) 詳情url地址ide

6.詳情頁面要提取的數據函數

# 博客標題工具

    # 博客建立時間

    # 博客url

    # 將url通過md5加密生成id

    # 縮略圖的地址

    # 圖片保存路徑  #這個屬性考慮好在哪賦值

    # 點贊數

    # 收藏數

    # 評論數

    # 博客做者

# 博客標籤

7.將圖片下載,保存到imgs文件夾中

8.將爬取的全部數據存儲到數據庫

建立項目咱們在cmd中進行建立,在開始以前咱們要將數據庫表以及其中字段建立好。


spider爬蟲代碼:

# -*- coding: utf-8 -*-
import scrapy
import re
# ..上級目錄
from ..items import BoleItem
# .同級目錄
from .tools import get_number,md5
# MVC  Model view controler
# 下降代碼耦合性
# MVVC


# 作url地址的拼接,若是沒有域名纔會進行拼接
import urlparse
class BoleSpider(scrapy.Spider):
    name = 'bole'
    allowed_domains = ['jobbole.com']
    start_urls = ['http://blog.jobbole.com/all-posts/']

    def parse(self, response):
        # 獲取每一頁的文章url和圖片url
        a_list = response.xpath(".//*[@id='archive']/div/div[1]/a")

        for a in a_list:
            # 獲取博客詳情頁面url
            # 獲取博客圖片的url
            a_href = a.xpath("@href").extract_first('')
            img_src = a.xpath("img/@src").extract_first('')

            yield scrapy.Request(
                url=a_href,
                callback=self.parse_detail,
                meta={"img_src":img_src}
            )
        next_page = response.xpath("//a[@class='next page-numbers']/@href").extract_first('')
        if next_page:
            # 發送請求,請求下一頁
            yield scrapy.Request(
                url=next_page
            )

    def parse_detail(self,response):
        # 博客詳情地址
        blog_url = response.url
        # 圖片url地址
        img_src = response.meta["img_src"]
        # 圖片地址有可能不完整
        if img_src:
            # 1.拼接URL地址
            img_src = urlparse.urljoin('http://www.jobbole.com',img_src)
        else:
            img_src = ''

        # 博客標題
        title = response.xpath("//div[@class='entry-header']/h1/text()").extract_first('')
        # 博客發佈時間
        blog_date = response.xpath("//p[@class='entry-meta-hide-on-mobile']/text()").extract_first('').strip().replace(u'·','').strip()
        # 全部的標籤
        tags = response.xpath("//p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
        # join 將列表中全部的字符串拼接,並以,隔開
        # split 將字符串根據某個字符進行分割,返回一個列表
        tags = ','.join(tags)
        # 點贊數
        like_count = response.xpath("//h10/text()").extract_first('')
        # 沒有點贊設置爲0
        if like_count:
            like_count = int(like_count)
        else:
            like_count = 0
        # 評論數
        comment_count = response.xpath("//a[@href='#article-comment']/span/text()").extract_first('')
        comment_count = get_number(comment_count)

        # 收藏數
        bookmark_count = response.xpath("//span[contains(@class,'bookmark-btn')]/text()").extract_first('')
        bookmark_count = get_number(bookmark_count)
        # blog_id
        # img_path
        # 建立Item對象
        item = BoleItem()
        item['blog_id'] = md5(response.url)
        item["title"] = title
        item["blog_url"] = blog_url
        # 將圖片url放在列表中
        item["img_src"] = [img_src]
        item["blog_date"] = blog_date
        item["tags"] = tags
        item["like_count"] = like_count
        item["comment_count"] = comment_count
        item["bookmark_count"] = bookmark_count
        print title,blog_date,blog_url,img_src,like_count,comment_count,bookmark_count,tags
        yield item 


建立工具包tools,優化代碼,工具包tools代碼:
import hashlib
# md5加密函數
def md5(str):
    import hashlib
    m = hashlib.md5()
    m.update(str)
    return m.hexdigest()

# 只要之後須要從字符串中匹配數字,就可使用這個函數
import re
def get_number(str):
    # 正則
    pattern = re.compile(r'\d+')
    rs = re.search(pattern, str)
    if rs:
        count = int(rs.group())
    else:
        count = 0
    return count

 
設置隨機請求頭,設置middlewares中內容
代碼以下:
# 設置隨機請求頭
from fake_useragent import UserAgent
class RandomUAMiddleware(object):
    def __init__(self,crawler):
        super(RandomUAMiddleware, self).__init__()
        self.crawler = crawler
        self.ua = UserAgent()
    @classmethod
    def from_crawler(cls,crawler):
        return cls(crawler)
    # 處理請求函數
    def process_request(self,request,spider):
        # 隨機產生請求頭
        request.headers.setdefault('User-Agent',self.ua.random)

在item中建立數據模型類
 
class BoleItem(scrapy.Item):
    title = scrapy.Field()
    blog_url = scrapy.Field()
    img_src = scrapy.Field()
    blog_date = scrapy.Field()
    tags = scrapy.Field()
    like_count = scrapy.Field()
    comment_count = scrapy.Field()
    bookmark_count = scrapy.Field()
    img_path = scrapy.Field()
    blog_id = scrapy.Field()


寫入數據庫,咱們使用異步寫入,首先在setting文件中本身配置一些項目信息

# 數據庫配置
MYSQL_HOST = '127.0.0.1'
MYSQL_PORT = 3306
MYSQL_USER = 'root'
MYSQL_PASSWD = '123456'
MYSQL_CHARSET = 'utf8'
MYSQL_DBNAME = 'jobbole'


而後設置pipelines,其中包含了咱們寫入數據庫的代碼,和下載圖片的代碼
代碼以下:
from scrapy.http.request import  Request
class MyImagePipeline(ImagesPipeline):

    # 把當前的item處理完成以後,執行這個函數
    def item_completed(self, results, item, info):
        # 首先判斷要下載的圖片是否有完成信息
        if results:
            try:
                # 檢測有有沒有圖片信息字典
                # 先取出列表中的元組,再從元組中取出字典
                img_dic = results[0][1]
                img_path = img_dic["path"]
            except Exception,e:
                print '------',e
                img_path = '沒有圖片路徑'
        else:
            img_path = '沒有圖片信息'
        # 對item的img_path賦值
        item["img_path"] = 'imgs/'+img_path
        # 返回item
        return item

    # 當須要處理媒體文件時,會執行該函數
    def get_media_requests(self, item, info):

        # 獲取圖片的url地址
        for src in item['img_src']:

            return Request(
                url=src,
                meta={'item':item}
            )
        # return [Request(x) for x in item["img_src"], []]

    # 指定圖片存放的路徑
    def file_path(self, request, response=None, info=None):
        # 取出item
        item = request.meta["item"]
        # 取出圖片url
        img_name = request.url.split('/')[-1]
        return img_name


# 異步寫入數據庫
from twisted.enterprise import adbapi
from MySQLdb import cursors

class MysqlTwistedPipeline(object):
    @classmethod
    # 這個函數會自動調用
    def from_settings(cls,settings):
        # 準備好鏈接數據庫須要的參數
        db_params = dict(
            # 複製當前行Ctrl + d
            host = settings["MYSQL_HOST"],
            port = settings["MYSQL_PORT"],
            user = settings["MYSQL_USER"],
            passwd = settings["MYSQL_PASSWD"],
            charset = settings["MYSQL_CHARSET"],
            db = settings["MYSQL_DBNAME"],
            use_unicode = True,
            # 指定遊標類型
            cursorclass=cursors.DictCursor
        )
        # 建立鏈接池
        # 1.要鏈接的名稱  2.鏈接須要的參數
        db_pool = adbapi.ConnectionPool('MySQLdb',**db_params)
        # 返回當前類的對象,而且把db_pool賦值給該類的對象
        return cls(db_pool)

    def __init__(self,db_pool):
        # 賦值
        self.db_pool = db_pool

    # 處理item函數
    def process_item(self,item,spider):
        # 把要處理的事件進行異步處理
        # 1.要處理的事件函數
        # 2.事件函數須要的參數
        query = self.db_pool.runInteraction(self.do_insert,item)
        # 執行sql出現錯誤信息
        query.addErrback(self.handle_error,item,spider)
    # 錯誤的緣由
    def handle_error(self,failure,item,spider):

        print failure

    # 處理插入數據庫的操做
    # cursor該函數是鏈接數據庫的函數,而且放在異步去執行,cursor執行sql語句
    def do_insert(self,cursor,item):
        # 1.準備sql語句
        sql = 'insert into bolejb(title,blog_url,img_src,blog_date,tags,like_count,comment_count,bookmark_count,img_path,blog_id)VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
        # 2.用cursor遊標執行sql
        cursor.execute(sql, (item["title"], item["blog_url"], item["img_src"][0], item["blog_date"], item["tags"], item["like_count"],item["comment_count"], item["bookmark_count"], item["img_path"],item["blog_id"]))


最後咱們須要設置咱們setting文件中的內容,將咱們重寫的中間件在setting中進一步設置

DOWNLOADER_MIDDLEWARES = {
   'BoleSpider.middlewares.RandomUAMiddleware': 1,
}

ITEM_PIPELINES = {
   'BoleSpider.pipelines.MyImagePipeline': 1,
   'BoleSpider.pipelines.MysqlTwistedPipeline': 2,
}
# 根據哪一個屬性下載圖片
IMAGES_URLS_FIELD = 'img_src'
IMAGES_STORE = 'imgs'

爲了方便調試,咱們能夠建立一個debug文件

from scrapy.cmdline import execute
execute(['scrapy','crawl','bole'])
相關文章
相關標籤/搜索