卷積神經網(wǎng)絡識別驗證碼模擬登錄正方教務系統(tǒng)的嘗試

簡述

所在的組織 華工小燈神 需要為用戶提供方便地獲取成績單的服務,于是需要爬取本科生的教務系統(tǒng),識別驗證碼時使用tesseract正確率太低,于是選擇mnist_cnn里的卷積神經網(wǎng)絡模型進行預測,正確率約98%白群。代碼見github

基本爬蟲流程

  • 本科生教務系統(tǒng)是經典的方正系統(tǒng)硬霍,網(wǎng)上已有不少不錯的爬蟲實現(xiàn)川抡,如python爬蟲實戰(zhàn)之模擬正方教務系統(tǒng)登錄查詢成績python爬蟲正方教務系統(tǒng)须尚。
  • 但是不同學校可能還是會有點區(qū)別侍咱,為了弄清具體的請求過程還是借了個賬號走了下請求流程耐床,即登錄、查看歷史成績楔脯,并用fiddler分析網(wǎng)絡請求的具體細節(jié)撩轰,fiddler在分析這種http請求時非常好用(chrome控制臺還是差點)。所有請求頭昧廷、cookies堪嫂、表單內容等都一覽無余,在使用python模擬請求時也可以用fiddler方便地查看返回頁面內容木柬。
  • 可以看到在訪問教務處主頁http://www.scut.edu.cn/jw2005/時皆串,實際上訪問的是http://xsweb.scuteo.com/default2.aspx,而它為我們自動跳轉到另一個路由眉枕,形式是/(20位標識符)/default2.aspx(即下面代碼中用到的url_login)恶复,其中20位唯一標識符就是用來標志此次會話怜森,某種程度上它相當于cookies。
  • 請求時要注意先get訪問一遍獲取csrf_token(即表單中的__VIEWSTATE參數(shù))谤牡,然后再連同其它參數(shù)一起post請求副硅,其中中文參數(shù)是以gbk編碼。登錄時的post請求如下:
login_data = {
    '__VIEWSTATE': csrf_token,
    'txtUserName': student_id,
    'TextBox2': password,
    'txtSecretCode': veri_code,
    'RadioButtonList1': '學生'.encode('gbk'),  # 表示學生登錄
    'Button1': '',
    'lbLanguage': '',
    'hidPdrs': '',
    'hidsc':  '',
}
res = requests.post(url_login, data=login_data)
  • 其中veri_code表示驗證碼翅萤,目前來看/default3.aspx恐疲、/default5.aspx等其他版本登錄頁面的可以繞過驗證碼的bug已經被修復,只能訪問http://xsweb.scuteo.com/(20位標識符)/CheckCode.aspx獲取驗證碼套么,最直接的解決方案就是采用顯示圖片培己,人工輸入的方式:
# 獲取驗證碼
import requests
base_url = 'http://xsweb.scuteo.com/(wmyrw345umteq0e0sh5rayez)/'
res = requests.get(base_url + 'CheckCode.aspx')

from PIL import Image
import io

# 1. 外部工具顯示
image_file = io.BytesIO(res.content)  # import io
image = Image.open(image_file)  # from PIL import Image
image.show()
veri_code = input('請輸入看到的驗證碼:')
# 2. ipython內嵌顯示
# from IPython.display import Image
# Image(data=res.content)
  • 登錄后就能獲取成績了,獲取所有成績的路由為http://xsweb.scuteo.com/(20位標識符)/xscjcx.aspx?xh=學生賬戶名&xm=學生姓名&gnmkdm=N121605违诗,學生姓名需要編碼漱凝,post表單內容直接將fiddler中看到的表單數(shù)據(jù)復制過來即可,當然有些參數(shù)也可不要诸迟。此處的請求頭中的referer參數(shù)必須要有茸炒,我使用的是http://xsweb.scuteo.com/(20位標識符)/xs_main.aspx?xh=學生賬戶名
  • 在獲取到所有成績頁面后阵苇,查找到成績所在的表格壁公,簡單解析后就可以得到需要的成績信息了。
soup = BeautifulSoup(res.text, "lxml")  # res為requests.post返回對象
table = soup.select_one('.datelist')
keys = [i.text for i in table.find('tr').find_all('td')]
scores = [
    dict(zip(
        keys, [i.text.strip() for i in tr.find_all('td')]))
    for tr in table.find_all('tr')[1:]]
print(sorted([i['成績'], i['課程名稱']] for i in scores))

驗證碼處理

# 備用嘗試1: 先灰度化,然后利用point函數(shù)對每個像素點操作來二值化掀亥,再中值濾波撞反,最后銳化圖像
from PIL import ImageFilter, ImageEnhance

imageL = image.convert("L").point(
    lambda i: i > 25, mode='1').filter(
        ImageFilter.MedianFilter(3)).convert('L')
imageS = ImageEnhance.Sharpness(imageL).enhance(2.0)
# 備用嘗試2: 由于驗證碼圖片是palette格式,所以要先轉換成RGB格式再濾波搪花,然后增強遏片,最后灰度、二值
im = image.convert('RGB').filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert("L").point(lambda i: i > 25, mode='1')
  • 在嘗試獲取一定數(shù)量的驗證碼后撮竿,發(fā)現(xiàn)其固定用RGB(0,0,153)做驗證字符顏色吮便,利用該特征可以很方便地去噪。
# 查看像素分布
import numpy as np
from collections import Counter
a = np.concatenate(np.array(image)) 
# 將圖片轉為矩陣幢踏,再從二維矩陣降為一維
unique, counts = np.unique(a, return_counts=True) 
# 對像素值索引計數(shù)髓需,也可使用Counter(a).most_common()
print(sorted(list(zip(counts, unique)), reverse=True)[:10])
# 對出現(xiàn)次數(shù)排序,并輸出前十個最多出現(xiàn)的
## 可以看到白色像素值索引255的出現(xiàn)最多
## 其次就是驗證碼的深藍色索引值為43
## 因為該驗證碼沒有模糊邊緣房蝉,直接取所有像素值索引為43的點就可拿到字符信息
好用的開源圖片處理工具 - GIMP
# 所以可以簡單地去噪
im = image.point(lambda i: i != 43, mode='1')
## 再選擇是否進行其他處理授账,如濾波枯跑、二值
from PIL import ImageFilter
im2 = im.convert('L').filter(ImageFilter.MedianFilter())
im2 = im2.point(lambda i: i > 25, mode='1'); im2
# 其他嘗試:
## 放大后濾波
im = image.resize((image.width*3, image.height*3), Image.ANTIALIAS)
im = im.convert('L').filter(ImageFilter.MedianFilter())
## 獲取輪廓
imC = im.filter(ImageFilter.CONTOUR); imC
## 更多的處理可使用scipy
## 可參考
## https://stackoverflow.com/questions/24687760/numpy-pil-python-crop-image-on-whitespace-or-crop-text-with-histogram-threshol
## http://www.scipy-lectures.org/advanced/image_processing/
## from scipy import ndimage

驗證碼識別

  • 在簡單的圖像處理后,就可以拿OCR工具來嘗試效果了白热。先試試開源的字符識別工具tesseract-ocr敛助。(tesseract參數(shù)列表)(注意修改下面的路徑為為自己的tesseract安裝路徑)
# 下載tesseract-ocr-setup.exe 安裝
# pip install pytesseract
import pytesseract
pytesseract.pytesseract.tesseract_cmd = 'D:\\Tesseract-OCR\\tesseract'
tessdata_dir_config = '--tessdata-dir "D:\\Tesseract-OCR\\tessdata"' # or TESSDATA_PREFIX
from functools import partial
convert = partial(pytesseract.image_to_string, lang='eng', config=tessdata_dir_config)
# 可以嘗試使用pip install tesserocr

# 使用下行代碼轉換
veri_code = convert(image)  # image 為驗證碼 (PIL.Image)
# 去掉識別出的奇怪的字符
import re
veri_code_ = re.sub('[^0-9a-zA-Z]+', '', veri_code)
print(veri_code, '->', veri_code_)
  • 可惜識別率太低,只有8%屋确∧苫鳎看來還是使用卷積神經網(wǎng)絡好了。
  • 使用Keras構建神經網(wǎng)絡模型非常簡單攻臀,且在github上可以方便地找到Keras的官方樣例焕数,最經典的就是手寫字符識別了,即mnist_cnn.py刨啸,和此次任務目標非常一致堡赔,直接拿來用,稍作修改即可设联。(另外還有簡單有趣的mnist_transfer_cnn.py
    (一些基本概念可參考:卷積神經網(wǎng)絡CNN基本概念筆記
  • 再簡單搜索下善已,找到這兩篇可以參考的不錯的博文:
    TensorFlow練習20: 使用深度學習破解字符驗證碼
    使用 Keras 來破解 captcha 驗證碼
  • 下面的問題就是構建訓練集了。由于正方教務系統(tǒng)返回的驗證碼格式固定离例,四個字符都是在固定的中心位置上進行了小范圍的旋轉换团,所以很好切割成單個字符,橫坐標的分界點為[5,17,29,41,53](單張驗證碼尺寸為72*27)宫蛆,至于字符之間有粘連影響的情況暫時不管艘包,故可以考慮簡單切割后識別單個字符。單個字符也便于自己生成訓練集耀盗。
im = image.point(lambda i: i != 43, mode='1')

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

a = np.array(im)
pd.DataFrame(a.sum(axis=0)).plot.line() # 畫出每列的像素累計值
plt.imshow(a) # 畫出圖像
split_lines = [5,17,29,41,53]
vlines = [plt.axvline(i, color='r') for i in split_lines] # 畫出分割線
plt.show()
  • 再去掉下面的一部分空白想虎,將每個字符切割成12*22(寬*高)的小塊
im = image.point(lambda i: i != 43, mode='1')
y_min, y_max = 0, 22 # im.height - 1 # 26
split_lines = [5,17,29,41,53]
ims = [im.crop([u, y_min, v, y_max]) for u, v in zip(split_lines[:-1], split_lines[1:])]
for i in range(4):
    plt.subplot(1,4,i+1)
    plt.imshow(images[i], interpolation='none')
plt.show()
  • 下面就是找一些字體,或直接利用windows系統(tǒng)自帶的字體叛拷,利用PIL的draw函數(shù)并隨機旋轉一定角度磷醋,生成一批類似大小的單個字符的訓練集,可惜沒找到一樣的字體效果的胡诗。
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
from PIL import ImageFilter, ImageEnhance

import random
import string
CHRS = string.ascii_lowercase + string.digits  # 小寫字母+數(shù)字的字符串列表

t_size = (12, 22) 
font_size = 20 # 即字體的高(大概值), 寬度約為一半
font_path = [  # 字體路徑
    r'E:/python/2017_9/Roboto-Regular.ttf',
    r'C:/Windows/Fonts/VERDANA.TTF',
    r'C:/Windows/Fonts/SIMKAI.TTF',
    r'C:/Windows/Fonts/SIMSUNB.TTF',
    r'C:/Windows/Fonts/REFSAN.TTF',
    r'C:/Windows/Fonts/MSYH.TTC',
]
fonts = [ImageFont.truetype(fp, font_size) for fp in font_path]
# font = ImageFont.truetype('E:/python/2017_9/simhei.ttf', font_size)

def gen_fake_code(prefix, c, font):
    txt = Image.new('L', t_size, color='black')
    ImageDraw.Draw(txt).text((0, -2), c, fill='white', font=font)
    w = txt.rotate(random.uniform(-20, 20), Image.BILINEAR) # center不指定,默認為中心點
    img_ = w.point(lambda i: i < 10, mode='1')
    # img_.show()
    img_.save(prefix + '_' + c + '.png')

# if __name__ == '__main__':
import os
os.chdir(r'E:\python\2017_9\fakes_single')
for c in CHRS:  # 對每一個字符的每一種字體生成200張
    for n, font in enumerate(fonts):
        for i in range(200):
            gen_fake_code(str(n)+'-'+str(i), c, font)
  • 對官方mnist-cnn樣例做簡單修改后就可以訓練我們自己的模型了淌友。
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
from PIL import Image
import numpy as np

import os
os.chdir(r'E:\python\2017_9\fakes_single')    # 跳轉到訓練集目錄
import string
CHRS = string.ascii_lowercase + string.digits  # 字符列表

num_classes = 36  # 共要識別36個字符(所有小寫字母+數(shù)字)煌恢,即36類
batch_size = 128
epochs = 12

# 輸入圖片的尺寸
img_rows, img_cols = 12, 22
# 根據(jù)keras的后端是TensorFlow還是Theano轉換輸入形式
if K.image_data_format() == 'channels_first':
    input_shape = (1, img_rows, img_cols)
else:
    input_shape = (img_rows, img_cols, 1)

import glob
X, Y = [], [] 
for f in glob.glob('*.png')[:]: # 遍歷當前目錄下所有png后綴的圖片
    t = 1.0 * np.array(Image.open(f))
    t = t.reshape(*input_shape) # reshape后要賦值
    X.append(t)  # 驗證碼像素列表
    
    s = f.split('_')[1].split('.')[0]  # 獲取文件名中的驗證碼字符
    Y.append(CHRS.index(s))  # 將字符轉換為相應的0-35數(shù)值

X = np.stack(X) # 將列表轉換為矩陣
Y = np.stack(Y)
# 此時Y形式為 array([26, 27, 28, ..., 23, 24, 25])

# 對Y值進行one-hot編碼 # 可嘗試 keras.utils.to_categorical(np.array([0,1,1]), 3) 理解
Y = keras.utils.to_categorical(Y, num_classes)

split_point = len(Y) - 720  # 簡單地分割訓練集與測試集
x_train, y_train, x_test, y_test = X[:split_point], Y[:split_point], X[split_point:], Y[split_point:]

# 以下模型和mnist-cnn相同
# 兩層3x3窗口的卷積(卷積核數(shù)為32和64),一層最大池化(MaxPooling2D)
# 再Dropout(隨機屏蔽部分神經元)并一維化(Flatten)到128個單元的全連接層(Dense)震庭,最后Dropout輸出到36個單元的全連接層(全部字符為36個)
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.Adadelta(),
              metrics=['accuracy'])

model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
# model.save(r'E:\python\2017_9\model_test.h5')
  • 可以看到很快就收斂了瑰抵,下面接著上面的環(huán)境測試一下,獲取新的驗證碼預測并將其保存
import requests
import io

os.chdir(r'E:\python\2017_9\modeltest')

def get_image():
    '''
    從教務處網(wǎng)站獲取驗證碼
    '''
    extra_code = 'azuuwd2ijh40vlnfg1ajdhbn'
    url_base = 'http://xsweb.scuteo.com/(%s)/' % extra_code
    # url_base = requests.get('http://xsweb.scuteo.com/default2.aspx').url.replace('default2.aspx', '')
    url_veri_img = url_base + 'CheckCode.aspx'
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',
    }
    res = requests.get(url_veri_img, headers=headers)
    image_file = io.BytesIO(res.content)
    image = Image.open(image_file)
    return image

def handle_split_image(image):
    '''
    切割驗證碼器联,返回包含四個字符圖像的列表
    '''
    im = image.point(lambda i: i != 43, mode='1')
    y_min, y_max = 0, 22 # im.height - 1 # 26
    split_lines = [5,17,29,41,53]
    ims = [im.crop([u, y_min, v, y_max]) for u, v in zip(split_lines[:-1], split_lines[1:])]
    # w = w.crop(w.getbbox()) # 切掉白邊 # 暫不需要
    return ims

def predict(images): 
    '''
    使用模型對四個字符的列表對應的驗證碼進行預測
    '''
    Y = []
    for i in range(4):
        im = images[i]
        test_input = np.concatenate(np.array(im))
        test_input = test_input.reshape(1, *input_shape)
        y_probs = model.predict(test_input)
        Y.append(CHRS[y_probs[0].argmax(-1)])
    return ''.join(Y)

def multi_process(x):
    '''
    獲取預測并保存圖片二汛,圖片名為預測值
    '''
    image = get_image()
    images = handle_split_image(image)
    image.save( predict(images) + '.png')


from multiprocessing.dummy import Pool

import datetime
now = datetime.datetime.now
start = now()
with Pool(30) as pool:
    pool.map(multi_process, [i for i in range(300)])
print('耗時 -> %s' % (now()-start))
  • 檢查一下正確率婿崭,約在40%,一些相似字符如f肴颊、t氓栈、l、1婿着、i等比較容易混淆授瘦,大概是訓練字體與實際獲取的不太一樣,且驗證碼切割時由于字符歪斜粘連會有某一字符的一部分出現(xiàn)在另一字符區(qū)域導致干擾竟宋。
  • 看來要提高正確率提完,還是得人工打碼,繼續(xù)獲取預測1000多個驗證碼并保存丘侠,在此基礎上進行人工打碼徒欣。在windows文件系統(tǒng)下重命名太不方便,圖片看著也太小蜗字,還是用python自帶的GUI庫tkinter做一個簡單的打碼助手打肝,順便統(tǒng)計之前預測的正確率和打碼花費時間,有些小bug秽澳,不過也湊合用闯睹。


# 該文件命名為dama.py 命令行pyinstaller -w -F dama.py 生成exe文件(dist目錄下)放入驗證碼所在文件夾執(zhí)行
import os
import glob
image_files = glob.glob('*.png')

from tkinter import *

root = Tk()

colours = ['green','orange','white']*2
labels = []
entrys = []
strvars = []

for r, c in enumerate(colours):
    l = Label(root, text=c, relief=RIDGE, width=34)
    l.grid(row=r,column=0)
    labels.append(l)
    
    v = StringVar(root, value='predict')
    strvars.append(v)
    
    e = Entry(root, textvariable=v, bg=c, relief=SUNKEN, width=10,
        font = "Helvetica 44 bold")
    e.grid(row=r,column=1)
    entrys.append(e)

info_label1 = Label(root, text='當前正確率', relief=RIDGE, width=34)
info_label1.grid(row=7,column=0)
info_label2 = Label(root, text='已用時間', relief=RIDGE, width=34)
info_label2.grid(row=7,column=1)

# ims = []
num = 0
cur_files = None

correct = 0
incorrect = 0

if not os.path.exists('new'):
    os.mkdir('new')

import datetime   
now = datetime.datetime.now
start = now()

from PIL import Image, ImageTk

def enter_callback(e):
    global num, cur_files
    global correct, incorrect
    
    if cur_files:
        for i in range(6):
            name = strvars[i].get()
            # print(name)
            
            if cur_files[i].split('.')[0] == name:
                correct += 1
            else:
                incorrect += 1
            try:
                os.rename(cur_files[i], ''.join(['new/', name, '.png']))
            except Exception as e:
                print(e)
            
        info1 = '當前正確率: %s' % (correct/(correct+incorrect))
        info2 = '已用時間: %s' % (now()-start)
        info_label1.config(text=info1)
        info_label2.config(text=info2)
    else:
        for i in range(6):
            labels[i].config(width=144)
        
    cur_files = image_files[num: num+6]

    for i in range(6):
        f = image_files[num+i]
        im = Image.open(f).resize((144, 54))
        im = ImageTk.PhotoImage(im)
        # https://stackoverflow.com/questions/18369936/how-to-open-pil-image-in-tkinter-on-canvas
        # im = PhotoImage(file=f)
        labels[i].configure(image=im)
        labels[i].image = im
        strvars[i].set(f.split('.')[0])
    num += 6

root.bind("<Return>", enter_callback)
root.mainloop()
  • 打碼了一千個后,利用上文模型繼續(xù)進行訓練
# 由人工打碼得到的訓練集進行訓練

os.chdir(r'E:\python\2017_9\modeltest\new')
# 檢查是否有打錯碼如打成3個字符的圖片
file_names = glob.glob('*.png')
error = [i for i in file_names if len(i.split('.')[0])!=4]
if error:
    print(error)
    raise Exception('打碼出錯担神,請檢查')

# 構造訓練集
X, Y = [], [] 
for f in file_names:
    image = Image.open(f)
    ims = handle_split_image(image)  # 打開并切割圖片

    name = f.split('.')[0]
    # 將圖片切割出的四個字符及其準確值依次放入列表
    for i, im in enumerate(ims): 
# 以下類同上文
        t = 1.0 * np.array(im)
        t = t.reshape(*input_shape)
        X.append(t)

        s = name[i]
        Y.append(CHRS.index(s))  # 驗證碼字符

X = np.stack(X)
Y = np.stack(Y)

Y = keras.utils.to_categorical(Y, num_classes)

split_point = len(Y) - 1000
x_train, y_train, x_test, y_test = X[:split_point], Y[:split_point], X[split_point:], Y[split_point:]

model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

# model.save('ok.h5')  # 保存模型
  • 由于訓練集很小楼吃,速度更快,多訓練幾回使得acc在0.99以上妄讯。


  • 最后孩锡,再去訪問新的教務處驗證碼進行測試,100張驗證碼大約有98張能完全識別對亥贸,這個正確率暫時足夠使用了躬窜。
  • 將以上內容簡單的整合在一起,模型加載采用keras.models.load_model方法獲取炕置,再抄點tkinter的UI荣挨,就可得到最終效果,代碼見github
  • 此外朴摊,利用flask和gevent可以構建一個簡單的web服務默垄,詳見README

其他資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市介杆,隨后出現(xiàn)的幾起案子鹃操,更是在濱河造成了極大的恐慌韭寸,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荆隘,死亡現(xiàn)場離奇詭異恩伺,居然都是意外死亡,警方通過查閱死者的電腦和手機臭胜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門莫其,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人耸三,你說我怎么就攤上這事乱陡。” “怎么了仪壮?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵憨颠,是天一觀的道長。 經常有香客問我积锅,道長爽彤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任缚陷,我火速辦了婚禮适篙,結果婚禮上,老公的妹妹穿的比我還像新娘箫爷。我一直安慰自己嚷节,他們只是感情好血柳,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布远寸。 她就那樣靜靜地躺著,像睡著了一般祖屏。 火紅的嫁衣襯著肌膚如雪窜护。 梳的紋絲不亂的頭發(fā)上效斑,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音柱徙,去河邊找鬼缓屠。 笑死,一個胖子當著我的面吹牛护侮,可吹牛的內容都是我干的敌完。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼概行,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了弧岳?” 一聲冷哼從身側響起凳忙,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤业踏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后涧卵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勤家,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年柳恐,在試婚紗的時候發(fā)現(xiàn)自己被綠了伐脖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡乐设,死狀恐怖讼庇,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情近尚,我是刑警寧澤蠕啄,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站戈锻,受9級特大地震影響歼跟,放射性物質發(fā)生泄漏。R本人自食惡果不足惜格遭,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一哈街、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拒迅,春花似錦骚秦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至往毡,卻和暖如春蒙揣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背开瞭。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工懒震, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗤详。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓个扰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親葱色。 傳聞我的和親對象是個殘疾皇子递宅,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容