Python学习:如何使用pandas分析excel数据(续)

1.问题

上篇,简单介绍了pandas库的使用,列出了常见操作的方法。本篇并不是继续讲述pandas库的使用,而是通过封装一个Excel类型,讲述如何封装第三方库,及其为何要封装。

2.方案

2.1.概述

Python的第三方库很多,很多库有官方文档,有的库文档齐全,直接使用也没多大问题。一般情况,我都会自己封装起来再使用,因为使用第三方库存在一定的风险。

封装的目的,主要有以下几个方面的考虑:

  • 封装降低复杂性,我们可能只需要库的部分功能,屏蔽用哪些用不到的功能,防止误用。
  • 封装提高可维护性和可替代性。第三方库可能有多个实现,也可能后续不再维护,如果代码到处充斥着第三方库的代码,后续维护比较麻烦。封装的好处,后续可以进行重新设计或者重构,上层调用的代码无需更改。
  • 封装可以降低学习成本,让团队其他成员无需直接面对第三方库。

继续我们的正题。封装pandas,类型的名字叫Excel,暂且只支持xlsxcsv文件,对于其他文件类型,留给读者自行封装。

下面代码要用到的模块,都罗列到这里:

import pandas as pd
import numpy as np

# 用于类型注解
from typing import List, Union

用于测试的数据如下:

排名 车系 官方价 品牌 销量
1 哈弗H6 9.80-15.49万 哈弗 376864
2 长安CS75 10.69-15.49万 长安汽车 26682
3 本田CR-V 16.98-27.68万 本田 249983
4 博越 8.98-14.68万 吉利汽车 24081
5 途观L 21.58-28.58万 大众 178574
6 奇骏 18.88-27.33万 日产 175177
7 RAV4荣放 17.48-25.98万 丰田 174940
8 探岳 18.69-26.49万 大众 169507
9 本田XR-V 12.79-17.59万 本田 168272
10 昂科威 18.99-23.99万 别克 167880

2.2.文件读写

pandas读写'xlsx'和'csv'文件,是用不同的函数实现。我们是基于用户使用场景进行封装,因此合并一个函数。代码如下:

class Excel:
    def __init__(self, df=None):
        self.df = df

    def read(self, file_name: str, sheet_name: str = "Sheet1", sep: str = ',', encoding='utf-8'):
        if file_name.endswith('.xlsx'):
            self.df = pd.read_excel(io=file_name, sheet_name=sheet_name)
        elif file_name.endswith('.csv'):
            self.df = pd.read_csv(io=file_name, sep=sep, encoding=encoding)
        else:
            raise Exception(
                f'just support csv and xlsx type file: {file_name}')

    def write(self, file_name: str, sheet_name: str = "Sheet1", sep: str = ',', encoding='utf-8'):
        if file_name.endswith('.xlsx'):
            self.df.to_excel(io=file_name, sheet_name=sheet_name)
        elif file_name.endswith('.csv', sep=sep, encoding=encoding):
            self.df.to_csv(file_name)
        else:
            raise Exception(
                f'just support csv and xlsx type file: {file_name}')
  • csv本身是文本文件,有编码方式的区别,还有默认是用逗号,分隔的。因此,因此开放了sepencoding参数,并且都提供了默认值。
  • csv文件,只能由一个sheet,实现的时候完全可以忽略'sheet'。
  • 为了简单起见,本文中xlsx文件只有一个sheet

2.3.头和数据

Excel表数据,一般分为表头和数据,我们可以单独设置两个属性,分别可以返回表头和数据。

class Excel:

    @property
    def headers(self):
        return self.df.column

    @property
    def values(self):
        #注意这里反馈的类型
        return self.df.values
  • 只用@property,是定义属性,有个好处是如果给属性赋值会出错。表头和表数据,我们不提供直接赋值的方法。

  • df.columndf.values都不是list类型,但都可以迭代。我们也可以转换成普通的类型返回,只是效率比较低。代码如下:

class Excel:

    @property
    def headers(self):
        return self.df.column.to_list()

    @property
    def values(self):
        ret = list()
        for row in self.df.values:
            col_data = list()
            for col in row:
                col_data.append(col)
            ret.append(col_data)
        return ret

验证代码:

xlsx = Excel()
xlsx.read('./data/2020-suv-top10.xlsx')
print(xlsx.headers)
print(xlsx.values)

输出结果如下:

['排名', '车系', '官方价', '品牌', '销量']
[[1 '哈弗H6' '9.80-15.49万' '哈弗' 376864]
 [2 '长安CS75 PLUS' '10.69-15.49万' '长安汽车' 266824]
 [3 '本田CR-V' '16.98-27.68万' '本田' 249983]
 [4 '博越' '8.98-14.68万' '吉利汽车' 240811]
 [5 '途观L' '21.58-28.58万' '大众' 178574]
 [6 '奇骏' '18.88-27.33万' '日产' 175177]
 [7 'RAV4荣放' '17.48-25.98万' '丰田' 174940]
 [8 '探岳' '18.69-26.49万' '大众' 169507]
 [9 '本田XR-V' '12.79-17.59万' '本田' 168272]
 [10 '昂科威' '18.99-23.99万' '别克' 167880]]

2.4.行列数据

2.4.1.数据获取

Excel数据操作,最直接的诉求,就是获取行数据和列数据,因此我们按照行或者列的维度进行封装。代码如下:

    def get_col_data(self, col_name: str):
        return self.df[col_name]

    def get_row_data(self, row_index: int):
        return self.df.iloc[row_index]

同样,我们可以提供返回普通类型的实现:

    def get_col_data(self, col_name: str):
        return self.df[col_name].to_list()

    def get_row_data(self, row_index: int):
        ret = list()
        for it in self.df.iloc[row_index]:
            ret.append(it)
        return ret
  • self.df.iloc[row_index]返回的字典类型,实际应用,获取行数据返回更偏向于返回一个list
  • for it in self.df.iloc[row_index]中的it,实际也不一定是普通类型,可以转换成str
  • pandas行数据获取,支持切片。获取行数据,同样可以设计成下面这种形式,是因为我们聚焦应用场景,不一定需要提供完整的切片使用方式,简单封装即可。
    def get_row_data(self, start, stop=None, step=None):
         return self.df.iloc[start:stop:step]
    

如果你是库的设计者,库会给很多人使用,倒是应该按照切片的方式封装,提高易用性。但,这也是双刃剑,提高易用性的同时,可能也提高了学习成本或者技术门槛。

每个语言都有自己的优秀特性,比如python的切片,用起来也很方便。但是,要完全支持切片,对于对python并不是很熟悉的情况下,要完全掌握切片的使用,对语言技能的要求会很高。因此封装的时候,要根据实际情况取舍,如果使用的人对python语言没有那么熟悉,简单封装即可。类似C语言的函数风格,容易理解,使用起来也不会很困难。

下面是验证代码:

xlsx = Excel()
xlsx.read('./data/2020-suv-top10.xlsx')
print(xlsx.get_col_data('品牌'))
print(xlsx.get_row_data(1))

输出如下:

['哈弗', '长安汽车', '本田', '吉利汽车', '大众', '日产', '丰田', '大众', '本田', '别克']
[2, '长安CS75 PLUS', '10.69-15.49万', '长安汽车', 266824]

2.4.2.数据修改

我们再来看行列数据的修改内容。修改行列数据的代码如下:

    def set_col_data(self, col_name: str, col_data: list):
        # 这里要确保col_data的长度与行数相当,可以增加检查
        for index, data in enumerate(col_data):
            self.df[col_name][index] = data

    def set_row_data(self, row_index: int, row_data: list):
        # 这里要使用Series类型(跟dict类似),row_data长度要与列个数相等
        new_row = pd.Series(row_data, self.headers)
        self.df.iloc[row_index] = new_row

链式调用越来越流行,我们封装的时候,可以考虑支持,只要让set函数返回对象的引用即可,当然也可以利用类型注解进行标注返回值。代码如下:

    def set_col_data(self, col_name: str, col_data: list) -> "Excel":
        # 这里要确保col_data的长度与行数相当,可以增加检查
        for index, data in enumerate(col_data):
            self.df[col_name][index] = data
        return self

    def set_row_data(self, row_index: int, row_data: list) -> "Excel":
        # 这里要使用Series类型(跟dict类似),row_data长度要与列个数相等
        new_row = pd.Series(row_data, self.headers)
        self.df.iloc[row_index] = new_row
        return self

测试代码下:

xlsx = Excel()
xlsx.read('./data/2020-suv-top10.xlsx')
print(xlsx.get_col_data('官方价'))
print(xlsx.get_row_data(2))

# 链式调用,可以一直调用下去
xlsx.set_col_data('官方价', ['0万' for x in range(10)]).set_row_data(2, [0, 0, 0, 0, 0])
print(xlsx.get_col_data('官方价'))
print(xlsx.get_row_data(2))

输出:

['9.80-15.49万', '10.69-15.49万', '16.98-27.68万', '8.98-14.68万', '21.58-28.58万', '18.88-27.33万', '17.48-25.98万', '18.69-26.49万', '12.79-17.59万', '18.99-23.99万']
[3, '本田CR-V', '16.98-27.68万', '本田', 249983]
['0万', '0万', 0, '0万', '0万', '0万', '0万', '0万', '0万', '0万']
[0, 0, 0, 0, 0]

2.4.3.数据插入

2.5.单元格数据

2.5.1.获取

单元格数据获取,比较简单,代码如下:

    def get_cell_data(self, row_index, col_index):
        return self.df.iloc[row_index][col_index]

验证代码:

xlsx = Excel()
xlsx.read('./data/2020-suv-top10.xlsx')
print(xlsx.get_cell_data(3,2))

输出如下:

8.98-14.68万

2.5.2.设置

单元格数据修改,代码如下:

    def set_cell_data(self, row_index, col_index, cell_data):
        self.df.iloc[row_index][col_index] = cell_data
        return self

不过问题是上述代码,并没有修改单元格的值,问题是
self.df.iloc[row_index][col_index]返回的是数据拷贝。

通过整数形式的行号和列号获取和设置数据,只是我们容易理解的方式。但是对于pandas库,单元格设置,其中可以是整数或字符串,但却是字符类型(类似字典dict),对于列是整数形式好像没有直接支持。列支持整数形式,可以使得列可以重名(excel中,并没有假定列名不能一样),基于应用两者最好都要支持。代码重新设计如下:

    def get_cell_data(self, row_index, col: Union[int, str]):
        if type(col) is int:
            return self.df.iloc[row_index][col]
        else:
            return self.df.at[row_index, col]

    def set_cell_data(self, row_index, col: Union[int, str], cell_data):
        if type(col) is int:
            self.df.at[row_index, self.headers[col]] = cell_data
        else:
            self.df.at[row_index, col] = cell_data

        return self

验证代码:

xlsx = Excel()
xlsx.read('./data/2020-suv-top10.xlsx')
print(xlsx.get_row_data(3))
xlsx.set_cell_data(3, 2, 'new data').set_cell_data(3, 3, 'haha')
print(xlsx.get_row_data(3))

输出:

[4, '博越', '8.98-14.68万', '吉利汽车', 240811]
[4, '博越', 'new data', 'haha', 240811]

2.6.数据筛选

pandas数据筛选是比较复杂也比较灵活。数据筛选,一般都是通过列进行筛选。

之前都是返回表行、列或者单元格数据,实际应用需要通过筛选一个列或者多个列,返回子表的数据,本节来实现这样的使用场景。

2.6.1.筛选

按照常用的过滤条件有=, >, >=, <, <=, in, not等,由于python不提供函数重载能力,因此用不同的函数来替代,代码如下:

    def filter_eq(self, col_name, col_data):
        df = self.df[self.df[col_name] == col_data]
        return Excel(df)

    def filter_not(self, col_name, col_data):
        df = self.df[self.df[col_name] != col_data]
        return Excel(df)

    def filter_lt(self, col_name, col_data, eq=False):
        if eq:
            df = self.df[self.df[col_name] <= col_data]
        else:
            df = self.df[self.df[col_name] < col_data]
        return Excel(df)

    def filter_gt(self, col_name, col_data, eq=False):
        if eq:
            df = self.df[self.df[col_name] >= col_data]
        else:
            df = self.df[self.df[col_name] > col_data]
        return Excel(df)

    def filter_in(self, col_name: str, col_data):
        df = self.df[col_name].str.contains(col_data, na=False, case=False)
        return Excel(self.df[df])

过滤函数返回Excel对象,方便实现链式调用,使用起来比较方便。但上述过滤函数封装,还有很多限制,比如只能单列过滤,每次过滤只能过滤一个条件等等。

针对多列过滤,比较复杂,本文就不打算继续实施,pandas库非常完善,有兴趣的同学可以参考。

上述有一些操作可能需要支持多个值过滤,比如=操作,可以用车系=大众|奥迪|斯柯达表示德国大众的过滤条件。这个值得我们封装起来使用。代码如下:

    def filter_eq(self, col_name, col_data):
        if type(col_data) is not list:
            df = self.df[self.df[col_name] == col_data]
            return Excel(df)

        df_list = list()
        for it in col_data:
            df = self.df[self.df[col_name] == it]
            df_list.append(df)

        # 这里用了合并数据,但是需要忽略index
        df_result = pd.concat(df_list, ignore_index=True)
        return Excel(df_result)

根据使用上的考虑,重新实现的过滤如下:

    def filter_eq(self, col_name, col_data: Union[any, list]):
        if type(col_data) is not list:
            df = self.df[self.df[col_name] == col_data]
            return Excel(df)

        df_list = list()
        for it in col_data:
            df = self.df[self.df[col_name] == it]
            df_list.append(df)

        # 需要忽略index
        df_result = pd.concat(df_list, ignore_index=True)
        return Excel(df_result)

    def filter_not(self, col_name, col_data):
        df = self.df[self.df[col_name] != col_data]
        return Excel(df)

    def filter_lt(self, col_name, col_data, eq=False):
        if eq:
            df = self.df[self.df[col_name] <= col_data]
        else:
            df = self.df[self.df[col_name] < col_data]
        return Excel(df)

    def filter_gt(self, col_name, col_data, eq=False):
        if eq:
            df = self.df[self.df[col_name] >= col_data]
        else:
            df = self.df[self.df[col_name] > col_data]
        return Excel(df)

    def filter_in(self, col_name: str, col_data: Union[str, List[str]]):
        if type(col_data) is not list:
            return Excel(self.df[self.df[col_name].str.contains(col_data, na=False, case=False)])

        con = ''
        for it in col_data:
            con = con + it + "|"
        con = con[:-1]
        return Excel(self.df[self.df[col_name].str.contains(con, na=False, case=False)])

    def filter_notin(self, col_name: str, col_data: Union[str, List[str]]):
        if type(col_data) is not list:
            return Excel(self.df[~self.df[col_name].str.contains(col_data, na=False, case=False)])

        con = ''
        for it in col_data:
            con = con + it + "|"
        con = con[:-1]
        return Excel(self.df[~self.df[col_name].str.contains(con, na=False, case=False)])

    def add(self, excel):
        df_list = [self.df, excel.df]
        # 需要忽略index
        df_result = pd.concat(df_list, ignore_index=True)
        return Excel(df_result)

测试代码,如下:

xlsx = Excel()
xlsx.read('./data/2020-suv-top10.xlsx')
print(xlsx.filter_eq('排名', [1, 2]).values)

print(xlsx.filter_eq('排名', 1).add(xlsx.filter_eq('排名', 2)).values)

输出:

[[1 '哈弗H6' '9.80-15.49万' '哈弗' 376864]
 [2 '长安CS75 PLUS' '10.69-15.49万' '长安汽车' 266824]]
[[1 '哈弗H6' '9.80-15.49万' '哈弗' 376864]
 [2 '长安CS75 PLUS' '10.69-15.49万' '长安汽车' 266824]]

总结

本文讲述pandas操作封装,并没有对参数进行检查。如果只是自己用,其实已经足够了。但是如果要是给团队使用的话,是远远不够的。传递错误了参数,运行出错,调用者直接面对低层被封装的第三方库的错误信息,定位可能比较麻烦。因此,很多时候,需要对参数进行错误检查。

本文涉及到的完整代码如下,供大家参考,应该基本达到日常使用程度。一些细节处理或者错误拦截,大家可以自行完善。

class Excel:
    """[excel和csv操作类型]
    """

    def __init__(self, df: pd.DataFrame = None):
        """[构造函数]

        Args:
            df ([DataFrame], optional): pandas.DataFrame类型
        """
        self.df = df

    @property
    def headers(self):
        """[表头属性]

        Returns:
            [list]: [返回表头]
        """
        return list(self.df.columns)

    @property
    def values(self):
        """[表数据获取]

        Returns:
            [numpy.ndarray]: [返回表数据]
        """
        return self.df.values

    def read(self, file_name: str, sheet_name: str = "Sheet1", sep: str = ',', encoding='utf-8'):
        """[读取excel或者csv文件]

        Args:
            file_name (str): [文件名]
            sheet_name (str, optional): [sheet明朝]. Defaults to "Sheet1".
            sep (str, optional): [分隔符,只有对csv文件有效]. 默认值','.
            encoding (str, optional): [csv文件的编解码]. 默认值'utf-8'.

        Raises:
            Exception: [文件不支持]
        """
        if file_name.endswith('.xlsx') or file_name.endswith('.xls'):
            self.df = pd.read_excel(io=file_name, sheet_name=sheet_name)
        elif file_name.endswith('.csv'):
            self.df = pd.read_csv(io=file_name, sep=sep, encoding=encoding)
        else:
            raise Exception(
                f'just support csv and xlsx type file: {file_name}')

    def write(self, file_name: str, sheet_name: str = "Sheet1", sep: str = ',', encoding='utf-8'):
        """[写入excel或者csv文件]

        Args:
            file_name (str): [文件名]
            sheet_name (str, optional): [sheet名称]. 默认值"Sheet1".
            sep (str, optional): [分隔符,只有对csv文件有效]. 默认值','.
            encoding (str, optional): [编解码]. 默认值'utf-8'.

        Raises:
            Exception: [文件类型不支持]
        """
        if file_name.endswith('.xlsx') or file_name.endswith('.xls'):
            self.df.to_excel(io=file_name, sheet_name=sheet_name)
        elif file_name.endswith('.csv', sep=sep, encoding=encoding):
            self.df.to_csv(file_name)
        else:
            raise Exception(
                f'just support csv and xlsx type file: {file_name}')

    def get_col_data(self, col_name: str):
        """[获取列数据]

        Args:
            col_name (str): [列名]

        Returns:
            [list]: [列数据]
        """
        return self.df[col_name].to_list()

    def get_row_data(self, row_index: int):
        """[获取行数据]

        Args:
            row_index (int): [行号]

        Returns:
            [list]: [一行数据]
        """
        ret = list()
        for it in self.df.iloc[row_index]:
            ret.append(it)
        return ret

    def set_col_data(self, col_name: str, col_data: list):
        """[设置列数据]

        Args:
            col_name (str): [类名]
            col_data (list): [列数据]

        Returns:
            [Excel]: [返回Excel对象]
        """
        # 这里要确保col_data的长度与行数相当,可以增加检查
        for index, data in enumerate(col_data):
            self.df[col_name][index] = data
        return self

    def set_row_data(self, row_index: int, row_data: list):
        """[设置一行数据]]

        Args:
            row_index (int): [行号]
            row_data (list): [行数据]

        Returns:
            [Excel]: [返回Excel对象]
        """
        # 这里要使用Series类型(跟dict类似),row_data长度要与列个数相等
        new_row = pd.Series(row_data, self.headers)
        self.df.iloc[row_index] = new_row
        return self

    def get_cell_data(self, row_index: int, col: Union[int, str]):
        """[获取单元格数据]

        Args:
            row_index ([int]): [行号]
            col (Union[int, str]): [列号,或者列名称]

        Returns:
            [any]: [数据]
        """
        if type(col) is int:
            return self.df.iloc[row_index][col]
        else:
            return self.df.at[row_index, col]

    def set_cell_data(self, row_index: int, col: Union[int, str], cell_data):
        """[summary]

        Args:
            row_index ([int]): [行号]
            col (Union[int, str]): [列号,或者列名称]
            cell_data ([type]): [单元格数据]

        Returns:
            [Excel]: [返回Excel对象]
        """
        # df的set_value方法好像改成_set_value,_开头标识私有,我们还是使用at方法
        if type(col) is int:
            self.df.at[row_index, self.headers[col]] = cell_data
        else:
            self.df.at[row_index, col] = cell_data

        return self

    def filter_eq(self, col_name, col_data: Union[any, list]):
        """[按照列值,按照相等过滤]

        Args:
            col_name ([str]): [列名]]
            col_data (Union[any, list]): [数据,或者数据]

        Returns:
            [Excel]: [返回Excel类型]
        """
        if type(col_data) is not list:
            df = self.df[self.df[col_name] == col_data]
            return Excel(df)

        df_list = list()
        for it in col_data:
            df = self.df[self.df[col_name] == it]
            df_list.append(df)

        # 需要忽略index
        df_result = pd.concat(df_list, ignore_index=True)
        return Excel(df_result)

    def filter_not(self, col_name, col_data: any):
        """[按照列值,进行!=条件过滤]

        Args:
            col_name ([str]): [列名]
            col_data ([any]): [值]

        Returns:
            [Excel]: [Excel对象]
        """
        df = self.df[self.df[col_name] != col_data]
        return Excel(df)

    def filter_lt(self, col_name, col_data, eq=False):
        """[按照列值,<或者<=过滤]

        Args:
            col_name ([str]): [列名]
            col_data ([any]): [值]
            eq (bool, optional): [=标志]. 默认值是False.

        Returns:
            [Excel]: [Excel对象]
        """
        if eq:
            df = self.df[self.df[col_name] <= col_data]
        else:
            df = self.df[self.df[col_name] < col_data]
        return Excel(df)

    def filter_gt(self, col_name, col_data, eq=False):
        """[按照列值,>或者>=过滤]

        Args:
            col_name ([str]): [列名]
            col_data ([any]): [值]
            eq (bool, optional): [=标志]. 默认值是False.

        Returns:
            [Excel]: [Excel对象]
        """
        if eq:
            df = self.df[self.df[col_name] >= col_data]
        else:
            df = self.df[self.df[col_name] > col_data]
        return Excel(df)

    def filter_in(self, col_name: str, col_data: Union[str, List[str]]):
        """[按照列值,通过包含条件过滤]

        Args:
            col_name (str): [列名]
            col_data (Union[str, List[str]]): [值,值list]]

        Returns:
            [Excel]: [Excel对象]
        """
        if type(col_data) is not list:
            return Excel(self.df[self.df[col_name].str.contains(col_data, na=False, case=False)])

        con = ''
        for it in col_data:
            con = con + it + "|"
        con = con[:-1]
        return Excel(self.df[self.df[col_name].str.contains(con, na=False, case=False)])

    def filter_notin(self, col_name: str, col_data: Union[str, List[str]]):
        """[按照列值,通过不包含条件过滤]

        Args:
            col_name (str): [列名]
            col_data (Union[str, List[str]]): [值,值list]]

        Returns:
            [Excel]: [Excel对象]
        """
        if type(col_data) is not list:
            return Excel(self.df[~self.df[col_name].str.contains(col_data, na=False, case=False)])

        con = ''
        for it in col_data:
            con = con + it + "|"
        con = con[:-1]
        return Excel(self.df[~self.df[col_name].str.contains(con, na=False, case=False)])

    def add(self, excel: 'Excel'):
        """[同一个表的,两个数据进行相加]

        Args:
            excel (Excel): [Excel对象]

        Returns:
            [Excel]: [返回Excel对象]
        """
        df_list = [self.df, excel.df]
        # 需要忽略index
        df_result = pd.concat(df_list, ignore_index=True)
        return Excel(df_result)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,888评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,677评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,386评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,726评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,729评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,337评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,902评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,807评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,349评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,439评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,567评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,242评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,933评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,420评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,531评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,995评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,585评论 2 359

推荐阅读更多精彩内容