Golang爬蟲全攻略

本文地址:http://www.reibang.com/p/4e53d4727152

一暴区、簡介

Golang誕生已經(jīng)超過十個(gè)年頭了耙饰,發(fā)展得愈發(fā)完善,其簡單方便的協(xié)程并發(fā)機(jī)制使得其在爬蟲領(lǐng)域有著一定的天賦桑谍。

首先我們來看一看铺董,Golang相對于Python這個(gè)爬蟲領(lǐng)域的傳統(tǒng)強(qiáng)者,有哪些優(yōu)點(diǎn)和缺點(diǎn)棱貌。

優(yōu)點(diǎn):

  • 完善簡便的協(xié)程并發(fā)機(jī)制
  • 并發(fā)數(shù)量大
  • 占用資源少
  • 運(yùn)行速度更快
  • 部署方便

缺點(diǎn):

  • 數(shù)據(jù)處理比較繁瑣
  • 成熟工具不是很多
  • 資料較少
  • 實(shí)現(xiàn)相同邏輯需要的代碼更多

由于Golang本身靜態(tài)語言的特性玖媚,和其特別的異常處理方式等等原因,在發(fā)起較復(fù)雜的請求時(shí)需要的代碼量自然會比Python多不少婚脱,但是其并發(fā)的數(shù)量也是遠(yuǎn)超Python的今魔,所以兩者應(yīng)用的場景并不十分相同,我們可以根據(jù)需要靈活的選擇障贸。

在剛剛接觸Golang的http包時(shí)错森,覺得其非常的方便,發(fā)起請求只需要一行代碼:

http.Get("https://www.baidu.com")

就算與Python的requests在便利方面也不遑多讓篮洁,然而在Golang勾起了我的興趣涩维,并深入接觸后,我發(fā)現(xiàn)并非如此袁波。最簡單的http.Get方法只能發(fā)起最簡單的請求瓦阐,一旦要設(shè)置headers、cookies等屬性時(shí)篷牌,需要寫的代碼會成幾何倍數(shù)上升睡蟋,而設(shè)置代理或者管理重定向等操作,會更加復(fù)雜枷颊。

這個(gè)摸索的過程中最痛苦的是戳杀,在網(wǎng)上能找到資料非常的稀少,大多數(shù)時(shí)候只能閱讀官方文檔和閱讀net標(biāo)準(zhǔn)庫的源碼夭苗。所幸Go語言的特性使得閱讀Go源碼是一件比較簡單的事信卡,相對于其他語言來說。

所以本篇文章的目的听诸,是為了讓那些使用Golang的朋友坐求,對如何使用Golang發(fā)起請求有一個(gè)比較全面的了解。

注1:Golang中文官網(wǎng)的文檔版本比較低晌梨,有些地方與最新版本不同桥嗤,有條件的同學(xué)可以爬爬梯子,去golang.org英文官網(wǎng)看文檔仔蝌。

注2:文中代碼為了簡潔泛领,省略掉了異常處理的部分,實(shí)際使用時(shí)需要按情況加上敛惊。


二渊鞋、簡單請求

Golang中的net包封裝了大部分網(wǎng)絡(luò)相關(guān)的功能,我們基本不需要借助其他庫就能實(shí)現(xiàn)我們的爬蟲需求。其中最為常用的是httpurl锡宋,使用前可以根據(jù)我們的需要進(jìn)行導(dǎo)入:

import (
    "net/http"
    "net/url"
)

http提供了一些非常方便的接口儡湾,可以實(shí)現(xiàn)最簡單的請求,例如Get执俩、Post徐钠、Head:

resp, err := http.Get("http://example.com/")
...
resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
...
resp, err := http.PostForm("http://example.com/form",
    url.Values{"key": {"Value"}, "id": {"123"}})

可以看到,我們非常簡單的就發(fā)起了請求并獲得了響應(yīng)役首,這里需要注意一點(diǎn)的是尝丐,獲得的響應(yīng)body需要我們手動關(guān)閉:

resp, err := http.Get("http://example.com/")
if err != nil {
    // 處理異常
}
defer resp.Body.Close()  // 函數(shù)結(jié)束時(shí)關(guān)閉Body
body, err := ioutil.ReadAll(resp.Body)  // 讀取Body
// ...

這樣的請求方式是非常方便的,但是當(dāng)我們需要定制我們請求的其他參數(shù)時(shí)衡奥,就必須要使用其他組件了爹袁。


三、Client

Clienthttp包內(nèi)部發(fā)起請求的組件矮固,使用它失息,我們才可以去控制請求的超時(shí)、重定向和其他的設(shè)置乏屯。以下是Client的定義:

type Client struct {
    Transport     RoundTripper
    CheckRedirect func(req *Request, via []*Request) error
    Jar           CookieJar
    Timeout       time.Duration // Go 1.3
}

首先是生成Client對象:

client := &http.Client{}

Client也有一些簡便的請求方法根时,如:

resp, err := client.Get("http://example.com")

但這種方法與直接使用http.Get沒多大差別,我們需要使用另一個(gè)方法來定制請求的Header辰晕、請求體蛤迎、證書驗(yàn)證等參數(shù),這就是RequestDo含友。

3.1. 設(shè)置超時(shí)

這是一張說明Client超時(shí)的控制范圍的圖:

client-timeout.png

這其中替裆,設(shè)置起來最方便的是http.Client.Timeout,可以在創(chuàng)建Client時(shí)通過字段設(shè)置窘问,其計(jì)算的范圍包括連接(Dial)到讀完response body為止辆童。

http.Client會自動跟隨重定向,重定向時(shí)間也會記入http.Client.Timeout惠赫,這點(diǎn)一定要注意把鉴。

client := &http.Client{
    Timeout: 15 * time.Second
}

還有一些更細(xì)粒度的超時(shí)控制:

  • net.Dialer.Timeout 限制建立TCP連接的時(shí)間
  • http.Transport.TLSHandshakeTimeout 限制 TLS握手的時(shí)間
  • http.Transport.ResponseHeaderTimeout 限制讀取response header的時(shí)間
  • http.Transport.ExpectContinueTimeout 限制client在發(fā)送包含 Expect: 100-continue的header到收到繼續(xù)發(fā)送body的response之間的時(shí)間等待。

如果需要使用這些超時(shí)儿咱,需要到Transport中去設(shè)置庭砍,方法如下所示:

c := &http.Client{  
    Transport: &http.Transport{
        DialContext: (&net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
        }).DialContext,
        TLSHandshakeTimeout:   10 * time.Second,
        ResponseHeaderTimeout: 10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    },
}

可以看到這其中沒有單獨(dú)控制Do方法超時(shí)時(shí)間的設(shè)置,如果需要的話可以使用context自行實(shí)現(xiàn)混埠。

3.2. 控制重定向

在Client的字段中怠缸,有一個(gè)CheckRedirect,此字段就是用來控制重定向的函數(shù)钳宪,如果沒有定義此字段的話揭北,將會使用默認(rèn)的defaultCheckRedirect方法扳炬。

默認(rèn)的轉(zhuǎn)發(fā)策略是最多轉(zhuǎn)發(fā)10次。

在轉(zhuǎn)發(fā)的過程中搔体,某一些包含安全信息的Header恨樟,比如AuthorizationWWW-Authenticate嫉柴、Cookie等厌杜,如果轉(zhuǎn)發(fā)是跨域的,那么這些Header不會復(fù)制到新的請求中计螺。

http的重定向判斷會默認(rèn)處理以下狀態(tài)碼的請求:

  • 301 (Moved Permanently)
  • 302 (Found)
  • 303 (See Other)
  • 307 (Temporary Redirect)
  • 308 (Permanent Redirect)

301、302和303請求將會改用Get訪問新的請求瞧壮,而307和308會使用原有的請求方式登馒。

那么,我們?nèi)绾稳タ刂浦囟ㄏ虻拇螖?shù)咆槽,甚至是禁止重定向呢陈轿?這里其實(shí)就需要我們自己去實(shí)現(xiàn)一個(gè)CheckRedirect函數(shù)了,首先我們來看看默認(rèn)的defaultCheckRedirect方法:

func defaultCheckRedirect(req *Request, via []*Request) error {
    if len(via) >= 10 {
        return errors.New("stopped after 10 redirects")
    }
    return nil
}

第一個(gè)參數(shù)req是即將轉(zhuǎn)發(fā)的request秦忿,第二個(gè)參數(shù) via是已經(jīng)請求過的requests麦射。可以看到其中的邏輯是判斷請求過的request數(shù)量灯谣,大于等于10的時(shí)候返回一個(gè)error潜秋,這也說明默認(rèn)的最大重定向次數(shù)為10次,當(dāng)此函數(shù)返回error時(shí)胎许,即是重定向結(jié)束的時(shí)候峻呛。

所以如果需要設(shè)置重定向次數(shù),那么復(fù)制一份這個(gè)函數(shù)辜窑,修改函數(shù)名字和其中if判斷的數(shù)字钩述,然后在生成Client時(shí)設(shè)定到Client即可:

client := &http.Client{
    CheckRedirect: yourCheckRedirect,
}

或者:

client := &http.Client{}
client.CheckRedirect = yourCheckRedirect

禁止重定向則可以把判斷數(shù)字修改為0。最好相應(yīng)地修改errors中提示的信息穆碎。

3.3. CookieJar管理

可以看到Client結(jié)構(gòu)體中還有一個(gè)Jar字段牙勘,類型為CookieJar,這是Client用來管理Cookie的對象所禀。

如果在生成Client時(shí)方面,沒有給這個(gè)字段賦值,使其為nil的話北秽,那么之后Client發(fā)起的請求將只會帶上Request對象中指定的Cookie葡幸,請求響應(yīng)中由服務(wù)器返回的Cookie也不會被保存。所以如果需要自動管理Cookie的話贺氓,我們還需要生成并設(shè)定一個(gè)CookieJar對象:

options := cookiejar.Options{
    PublicSuffixList: publicsuffix.List
}
jar, err := cookiejar.New(&options)
client := &http.Client{
    Jar: jar,
}

這里的publicsuffix.List是一個(gè)域的公共后綴列表蔚叨,是一個(gè)可選的選項(xiàng)床蜘,設(shè)置為nil代表不啟用。但是不啟用的情況下會使Cookie變得不安全:意味著foo.com的HTTP服務(wù)器可以為bar.com設(shè)置cookie蔑水。所以一般來說最好啟用邢锯。

如果嫌麻煩不想啟用PublicSuffixList,可以將其設(shè)置為nil搀别,如下即可:

jar, err := cookiejar.New(nil)
client := &http.Client{
    Jar: jar,
}

publicsuffix.List的實(shí)現(xiàn)位于golang.org/x/net/publicsuffix丹擎,需要額外下載,使用的時(shí)候也需要導(dǎo)入:

import "golang.org/x/net/publicsuffix"

四歇父、 Request

這是Golang源碼中Request定義的字段蒂培,可以看到非常的多,有興趣的可以去源碼或者官方文檔看有注釋的版本榜苫,本文只介紹一些比較重要的字段护戳。

type Request struct {
    Method           string
    URL              *url.URL
    Proto            string // "HTTP/1.0"
    ProtoMajor       int    // 1
    ProtoMinor       int    // 0
    Header           Header
    Body             io.ReadCloser
    GetBody          func() (io.ReadCloser, error)
    ContentLength    int64
    TransferEncoding []string
    Close            bool
    Host             string
    Form             url.Values
    PostForm         url.Values
    MultipartForm    *multipart.Form
    Trailer          Header
    RemoteAddr       string
    RequestURI       string
    TLS              *tls.ConnectionState
    Cancel           <-chan struct{}
    Response         *Response
}

在這里不推薦直接生成Request,而應(yīng)該使用http提供的NewRequest方法來生成Request垂睬,此方法中做了一些生成Request的默認(rèn)設(shè)置媳荒,以下是NewRequest的函數(shù)簽名:

func NewRequest(method, url string, body io.Reader) (*Request, error)

參數(shù)中methodurl兩個(gè)是必備參數(shù),而body參數(shù)驹饺,在使用沒有body的請求方法時(shí)钳枕,傳入nil即可。

配置好Request之后赏壹,使用Client對象的Do方法鱼炒,就可以將Request發(fā)送出去,以下是示例:

req, err := NewRequest("GET", "https://www.baidu.com", nil)
resp, err := client.Do(req)

4.1. Method

請求方法卡儒,必備的參數(shù)田柔,如果為空字符則表示Get請求。

注:Go的HTTP客戶端不支持CONNECT請求方法骨望。

4.2. URL

一個(gè)被解析過的url結(jié)構(gòu)體硬爆。

4.3. Proto

HTTP協(xié)議版本。

在Go中擎鸠,HTTP請求會默認(rèn)使用HTTP1.1缀磕,而HTTPS請求會默認(rèn)首先使用HTTP2.0,如果目標(biāo)服務(wù)器不支持劣光,握手失敗后才會改用HTTP1.1袜蚕。

如果希望強(qiáng)制使用HTTP2.0的協(xié)議,那么需要使用golang.org/x/net/http2這個(gè)包所提供的功能绢涡。

4.4. 發(fā)起Post請求

如果要使用Request發(fā)起Post請求牲剃,提交表單的話,可以用到它的PostForm字段雄可,這是一個(gè)類型為url.Values的字段凿傅,以下為示例:

req, err := NewRequest("Post", "https://www.baidu.com", nil)
req.PostForm.Add("key", "value")

如果你Post提交的不是表單數(shù)據(jù)缠犀,那么你需要將其封裝成io.Reader類型,并在NewRequest函數(shù)中傳遞進(jìn)去聪舒。

4.4. 設(shè)置Header

Header的類型是http.Header辨液,其中包含著之前請求中返回的header和client發(fā)送的header。

可以使用這種方式設(shè)置Header:

req, err := NewRequest("Get", "https://www.baidu.com", nil)
req.Header.Add("key", "value")

Header還有一些Set箱残、Del等方法可以使用滔迈。

4.5. 添加Cookie

前文我們已經(jīng)介紹了如何在Client中啟用一直使用的CookieJar,使用它可以自動管理獲得的Cookie被辑。

但很多時(shí)候我們也需要給特定的請求手動設(shè)置Cookie燎悍,這個(gè)時(shí)候就可以使用Request對象的AddCookie方法,這是其函數(shù)簽名:

func (r *Request) AddCookie(c *Cookie)

要注意的是盼理,其傳入的參數(shù)是Cookie類型间涵,,以下是此類型包含的屬性:

type Cookie struct {
    Name       string
    Value      string
    Path       string
    Domain     string
    Expires    time.Time
    RawExpires string
    MaxAge     int
    Secure     bool
    HttpOnly   bool
    Raw        string
    Unparsed   []string
}

其中只有NameValue是必須的榜揖,所以以下是添加Cookie的示例:

c := &http.Cookie{
    Name:  "key",
    Value: "value",
}
req.AddCookie(c)

五、Transport

TransportClient中的一個(gè)類型抗蠢,用于控制傳輸過程举哟,是Client實(shí)際發(fā)起請求的底層實(shí)現(xiàn)。如果沒有給這個(gè)字段初始化相應(yīng)的值迅矛,那么將會使用默認(rèn)的DefaultTransport妨猩。

Transport承擔(dān)起了Client中連接池的功能,它會將建立的連接緩存下來秽褒,這可能會在訪問大量不同網(wǎng)站時(shí)壶硅,留下太多打開的連接,這可以使用Transport中的方法進(jìn)行關(guān)閉销斟。

首先來看一下Transport的定義:

type Transport struct {
    Proxy                  func(*Request) (*url.URL, error)
    DialContext            func(ctx context.Context, network, addr string) (net.Conn, error) // Go 1.7
    Dial                   func(network, addr string) (net.Conn, error)
    DialTLS                func(network, addr string) (net.Conn, error) // Go 1.4
    TLSClientConfig        *tls.Config
    TLSHandshakeTimeout    time.Duration // Go 1.3
    DisableKeepAlives      bool
    DisableCompression     bool
    MaxIdleConns           int // Go 1.7
    MaxIdleConnsPerHost    int
    MaxConnsPerHost        int                                                         // Go 1.11
    IdleConnTimeout        time.Duration                                               // Go 1.7
    ResponseHeaderTimeout  time.Duration                                               // Go 1.1
    ExpectContinueTimeout  time.Duration                                               // Go 1.6
    TLSNextProto           map[string]func(authority string, c *tls.Conn) RoundTripper // Go 1.6
    ProxyConnectHeader     Header                                                      // Go 1.8
    MaxResponseHeaderBytes int64                                                       // Go 1.7
}

由于TransportClient內(nèi)部請求的實(shí)際發(fā)起者庐椒,所以內(nèi)容會比較多,1.6之后的版本也添加了許多新的字段蚂踊,這里我們來講解常見的一些字段约谈。

5.1. 撥號

由于Client中設(shè)置的Timeout范圍比較寬,而在生產(chǎn)環(huán)境中我們可能需要更為精細(xì)的超時(shí)控制犁钟,在Dial撥號中可以設(shè)置幾個(gè)超時(shí)時(shí)間棱诱。

在較新的版本中,Dial這個(gè)字段已經(jīng)不再被推薦使用涝动,取而代之的是DialContext迈勋,設(shè)置這個(gè)字段,需要借助于net.Dialer醋粟,以下是其定義:

type Dialer struct {
    Timeout time.Duration
    Deadline time.Time
    LocalAddr Addr
    DualStack bool
    FallbackDelay time.Duration
    KeepAlive time.Duration
    Resolver *Resolver
    Cancel <-chan struct{}
    Control func(network, address string, c syscall.RawConn) error
}

這其中需要我們設(shè)置的并不多靡菇,主要是Timeout和KeepAlive重归。Timeout是Dial這個(gè)過程的超時(shí)時(shí)間,而KeepAlive是連接池中連接的超時(shí)時(shí)間镰官,如下所示:

trans := &http.Transport{
    DialContext: (&net.Dialer{
        Timeout: 30 * time.Second,
        KeepAlive: 30 * time.Second,
    }).DialContext,
}

5.2. 設(shè)置代理

Transport第一個(gè)Proxy字段是用來設(shè)置代理提前,支持HTTP、HTTPS泳唠、SOCKS5三種代理方式狈网,首先我們來看看如何設(shè)置HTTP和HTTPS代理:

package main

import (
    "net/url"
    "net/http"
)

func main() {
    proxyURL, _ := url.Parse("https://127.0.0.1:1080")
    trans := &http.Transport{
        Proxy: http.ProxyURL(proxyURL),
    }
    client := &http.Client{
        Transport: trans,
    }
    client.Get("https://www.google.com")
}

設(shè)置SOCKS5代理則需要借助golang.org/x/net/proxy

package main

import (
    "net/url"
    "net/http"
    "golang.org/x/net/proxy"
)

func main() {
    dialer, err := proxy.SOCKS5("tcp", "127.0.0.1:8080",
        &proxy.Auth{User:"username", Password:"password"},
        &net.Dialer {
            Timeout: 30 * time.Second,
            KeepAlive: 30 * time.Second,
        },
    )
    trans := &http.Transport{
        DialContext: dialer.DialContext
    }
    client := &http.Client{
        Transport: trans,
    }
    client.Get("https://www.google.com")
}

這里的proxy.SOCKS5函數(shù)將會返回一個(gè)Dialer對象,其傳入的參數(shù)分別為協(xié)議笨腥、IP端口拓哺、賬號密碼、Dialer脖母,如果代理不需要賬號密碼驗(yàn)證的話士鸥,第三個(gè)字段可以設(shè)置為nil

5.3. 連接控制

眾所周知谆级,HTTP1.0協(xié)議使用的是短連接烤礁,而HTTP1.1默認(rèn)使用的是長連接,使用長連接則可以復(fù)用連接肥照,減少建立連接的開銷脚仔。

Transport中實(shí)現(xiàn)了連接池的功能,可以將連接保存下來以便下次訪問此域名舆绎,其中也對連接的數(shù)量做出了一定的限制鲤脏。

DisableKeepAlives這個(gè)字段可以用來關(guān)閉長連接,默認(rèn)值為false吕朵,如果有特殊的需求猎醇,需要使用短連接,可以設(shè)置此字段為true:

trans := &http.Transport{
    ...
    DisableKeepAlives: true,
}

除此之外努溃,還可以控制連接的數(shù)量和保持時(shí)間:

  1. MaxConnsPerHost int - 每個(gè)域名下最大連接數(shù)量硫嘶,包括正在撥號的、活躍的茅坛、空閑的的連接音半。

    值為0表示不限制數(shù)量。

  2. MaxIdleConns int - 空閑連接的最大數(shù)量贡蓖。

    DefaultTransport中的默認(rèn)值為100曹鸠,在需要發(fā)起大量連接時(shí)偏小,可以根據(jù)需求自行設(shè)定斥铺。

    值為0表示不限制數(shù)量彻桃。

  3. MaxIdleConnsPerHost int - 每個(gè)域名下空閑連接的最大數(shù)量。

    值為0則會使用默認(rèn)的數(shù)量晾蜘,每個(gè)域名下只能有兩個(gè)空閑連接邻眷。在對單個(gè)網(wǎng)站發(fā)起大量連接時(shí)霞丧,兩個(gè)連接可能會不夠臀突,可以酌情增加此數(shù)值料祠。

  4. IdleConnTimeout time.Duration - 空閑連接的超時(shí)時(shí)間储玫,從每一次空閑開始算。DefaultTransport中的默認(rèn)值為90秒驯镊。

    值為0表示不限制葫督。

由于Transport負(fù)擔(dān)起了連接池的功能,所以在并發(fā)使用時(shí)板惑,最好將Transport與Client一起復(fù)用橄镜,不然可能會造成發(fā)起過量的長連接,浪費(fèi)系統(tǒng)資源冯乘。


六洽胶、其他

6.1. 設(shè)置url參數(shù)

在Go的請求方式中,沒有給我們提供可以直接設(shè)置url參數(shù)的方法裆馒,所以需要我們自己在url地址中進(jìn)行拼接姊氓。

url包中提供了一個(gè)url.Values類型,其本質(zhì)的類型為:map[string][]string喷好,可以讓我們拼接參數(shù)更加簡單他膳,如下所示:

URL := "http://httpbin.org/get"
params := url.Values{
    "key1": {"value"},
    "key2": {"value2", "value3"},
}
URL = URL + "&" + params.Encode()
fmt.Println(URL)
// 輸出為:http://httpbin.org/get&key1=value&key2=value2&key2=value3

七、總結(jié)

總的來說绒窑,Go語言中內(nèi)置的標(biāo)準(zhǔn)庫功能是比較完善的,如果要寫一個(gè)客戶端的話舔亭,基本不需要用到標(biāo)準(zhǔn)庫之外的內(nèi)容些膨,其可以控制的請求細(xì)節(jié)也比較多。

但相較于Python的Requests這類庫钦铺,需要寫的代碼依然要多非常多订雾,再加上特別的異常處理機(jī)制,在請求過程中要寫大量的異常檢查語句矛洞。需要使用的朋友可以考慮先將請求和異常處理的部分封裝以后使用洼哎。


八、示例

以下是發(fā)起Get請求的一個(gè)例子:

// 生成client客戶端
client := &http.Client{}
// 生成Request對象
req, err := http.NewRequest("Get", "http://httpbin.org/get", nil)
if err != nil {
    fmt.Println(err)
}
// 添加Header
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36")
// 發(fā)起請求
resp, err := client.Do(req)
if err != nil {
    fmt.Println(err)
}
// 設(shè)定關(guān)閉響應(yīng)體
defer resp.Body.Close()
// 讀取響應(yīng)體
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
    fmt.Println(err)
}
fmt.Println(string(body))

參考:

https://www.cnblogs.com/WingPig/p/5929138.html

http://mengqi.info/html/2015/201506062329-socks5-proxy-client-in-golang.html

https://colobu.com/2016/07/01/the-complete-guide-to-golang-net-http-timeouts/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沼本,一起剝皮案震驚了整個(gè)濱河市噩峦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抽兆,老刑警劉巖识补,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辫红,居然都是意外死亡凭涂,警方通過查閱死者的電腦和手機(jī)祝辣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來切油,“玉大人蝙斜,你說我怎么就攤上這事∨旌” “怎么了孕荠?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滤馍。 經(jīng)常有香客問我岛琼,道長,這世上最難降的妖魔是什么巢株? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任槐瑞,我火速辦了婚禮,結(jié)果婚禮上阁苞,老公的妹妹穿的比我還像新娘困檩。我一直安慰自己,他們只是感情好那槽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布悼沿。 她就那樣靜靜地躺著,像睡著了一般骚灸。 火紅的嫁衣襯著肌膚如雪糟趾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天甚牲,我揣著相機(jī)與錄音义郑,去河邊找鬼。 笑死丈钙,一個(gè)胖子當(dāng)著我的面吹牛非驮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雏赦,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼劫笙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了星岗?” 一聲冷哼從身側(cè)響起填大,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俏橘,沒想到半個(gè)月后栋盹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年例获,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了汉额。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡榨汤,死狀恐怖蠕搜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情收壕,我是刑警寧澤妓灌,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蜜宪,受9級特大地震影響虫埂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜圃验,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一掉伏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧澳窑,春花似錦斧散、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至麻裁,卻和暖如春箍镜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背煎源。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工鹿寨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人薪夕。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像赫悄,于是被迫代替她去往敵國和親原献。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容