Python Scrapy 爬虫教程之选择器 Selectors

Selectors

在抓取一个web页面的时候,大多数任务在于从HTML源中提取数据。有很多可用的的库支持这些操作,比如:

  • BeautifulSoup:这是一个在 Python 编程中非常流行的网页抓取库,它依照HTML代码的结构来构造 Python 对象,并且能正确处理不良标记,但是它有一个弱项:太慢
  • lxml:以非常 python 化的 ElementTree 接口为基础,建立 XML 解析库(同时也能解析 HTML)

Scrapy 有自己的提取数据的机制。它们称之为 selectors(选择器),因为从 HTML 文档中筛选特定内容,可以使用XPathCSS表达式。

XPath是一个筛选 XML 文档节点的语言,也能用于筛选 HTML 文档。
CSS 是一个应用格式到 HTML 文档的语言,它定义选择器与特定 HTML 元素格式之间的关系。

注意:
Scrapy 选择器是 `parsel` 库的轻量级再封装;封装的目的是为了与 Scrapy 响应对象提供更好的整合。
`parsel` 是一个独立的网页抓取库,可以不依赖于 Scrapy。它使用 `lxml` 库作为底层引擎,并且在 lxml 顶层接口再实现了一个简单API。这意味着 Scrapy 选择器的速度与解析准确性与 lxml 十分接近。

一、使用选择器

scrapy shell https://docs.scrapy.org/en/latest/_static/selectors-sample1.html 为例

1.1 构建选择器

使用 .selector 暴露 Response 对象的 Selector 实例:

In [4]: response.selector.xpath('//a/text()').getall()
Out[4]:
['Name: My image 1 ',
 'Name: My image 2 ',
 'Name: My image 3 ',
 'Name: My image 4 ',
 'Name: My image 5 ']

更简洁的方式查询,Scrapy 提供了两种简写:response.xpath()response.css()

In [5]: response.css('a::text').getall()
In [6]: response.xpath('//a/text()').getall()

Scrapy selectors 是 Selector 类的实例,通过传输 TextResponse 对象或作为 unicode 的补全来构成。
通常不需要手动构造 Scrapy selectors,原因如下:response 对象可以用于 Spider 回调,所以大部分场景下会偏向于使用 response.css()response.xpath() 作为简写。通过使用 response.selectorresponse.xpath() response.css() 可以确保响应体只被解析一次。

todo:这里的 Selector 类 和 Spider 与 response 之间的回调 问题,以后在研究源码的时候着重看看怎么处理的

但是在必要条件下,需要直接使用 Selector ,比如要从如下的内容中构造对象:

>>> from scrapy.selector import Selector
>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').get()
'good'

Selector 会自动基于输入类型选择最好的解析规则(XML 或 HTML)。

1.2 选择器

接下来先介绍如何使用Scrapy shell(它提供了可交互的测试)
使用方法scrapy shell https://docs.scrapy.org/en/latest/_static/selectors-sample1.html

在 shell 加载完成后,可以通过 response 直接获取响应,通过 response.selector 属性获取选择器

由于该网页返回的是 HTML 内容,所以选择器会自动使用 HTML 解析模式。

为title标签内的文本构造 XPath 访问方式

In [7]: response.xpath('//title/text()')
Out[7]: [<Selector xpath='//title/text()' data='Example website'>]

可以看到返回的是一个选择器对象列表,如果想要提取文本内容,需要使用选择器的 .get() 或者 .getall() 方法,如下所示:

In [8]: response.xpath('//title/text()').get()
Out[8]: 'Example website'

.get() 永远只返回一个结果;

如果选择器有多个匹配,那么只返回匹配的第一个内容;
如果只有没有匹配,将返回 None

.getall() 返回一个结果列表

注意 CSS 选择器可以使用 CSS3 的伪元素(pseudoelements)选择文本内容和属性节点。

In [9]: response.css('title::text')
Out[9]: [<Selector xpath='descendant-or-self::title/text()' data='Example website'>]

In [10]: response.css('title::text').get()
Out[10]: 'Example website'

可以得知,.xpath().css() 方法都会返回一个 SelectorList 实例对象,该实例对象是选择器的列表。

这个接口还可以用于快速选择嵌套的数据:

In [11]: response.css('img').xpath('@src').getall()
Out[11]:
['image1_thumb.jpg',
 'image2_thumb.jpg',
 'image3_thumb.jpg',
 'image4_thumb.jpg',
 'image5_thumb.jpg']

自定义返回结果

如果在获取元素时,没有发现对应的元素,那么将返回 None;但是也可以自定义返回结果:

In [12]: response.css('ab_post').get('no_element')
Out[12]: 'no_element'

css 获取属性值 .attrib

在上面的例子中,获取 src 属性使用了 ‘@src’ XPath,在 CSS 中通过 .attrib 也可以查询该属性。

In [13]: [img.attrib['src'] for img in response.css('img')]
Out[13]:
['image1_thumb.jpg',
 'image2_thumb.jpg',
 'image3_thumb.jpg',
 'image4_thumb.jpg',
 'image5_thumb.jpg']

.attrib 作为简写同样也可以直接获取 SelectorList, 它返回第一个被匹配的元素:

In [14]: response.css('img').attrib['src']
Out[14]: 'image1_thumb.jpg'

这种做法在只需要一个结果时非常有用,比如当根据id或页面上唯一的元素选择时:

In [18]: response.css('base').attrib['href']
Out[18]: 'http://example.com/'

综合用法

获取 base 标签的 href 属性内容

In [18]: response.css('base').attrib['href']
Out[18]: 'http://example.com/'

In [19]: response.xpath('//base/@href').get()
Out[19]: 'http://example.com/'

In [20]: response.css('base::attr(href)').get()
Out[20]: 'http://example.com/'

In [21]: response.css('base').attrib['href']
Out[21]: 'http://example.com/'

获取标签的 href 属性内容(限定内容包含 “image”)

In [24]: response.xpath('//a[contains(@href, "image")]/@href').getall()
Out[24]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

In [25]: response.css('a[href*=image]::attr(href)').getall()
Out[25]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

获取标签 href 内容包含“image”的子标签 src 属性值

In [26]: response.xpath('//a[contains(@href, "image")]/img/@src').getall()
Out[26]:
['image1_thumb.jpg',
 'image2_thumb.jpg',
 'image3_thumb.jpg',
 'image4_thumb.jpg',
 'image5_thumb.jpg']

In [27]: response.css('a[href*=image] img::attr(src)').getall()
Out[27]:
['image1_thumb.jpg',
 'image2_thumb.jpg',
 'image3_thumb.jpg',
 'image4_thumb.jpg',
 'image5_thumb.jpg']

1.3 扩展CSS选择器

根据 W3C 标准,CSS 选择器不能提供选择文本节点或属性值的功能。
但是在网页中抓取内容是非常必要的,Scrapy 为此实现了许多非标准的伪元素。

  • 选择文本节点,使用 ::text
  • 选择属性值,使用 ::attr(name) 这里的 name 属性对应的名称

实例

  • title::text 筛选 <title> 元素的子节点文本
  • *::text 筛选当前选择器上下文的所有子文本节点
  • foo::text 如果 foo 元素存在但是不包含文本,那么返回结果为空。
    • 这意味着 .css('foo::text').get() 即使元素存在也返回 None,如果需要修改返回值,可以添加 *.get(default='')
  • a::attr(href) 筛选 href 属性的值

1.4 嵌套选择器

筛选模式( .xapth().css() )都返回同一类型选择器的列表,所以你可以对这些筛选列表进行调用,下面是例子:

In [28]: links = response.xpath('//a[contains(@href, "image")]')

In [29]: links.getall()
Out[29]:
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>',
 '<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>',
 '<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>',
 '<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>',
 '<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']

In [30]: for index, link in enumerate(links):
    ...:     args = (index, link.xpath('@href').get(), link.xpath('img/@src').get())
    ...:     print('Link numver %d points to url %r and image %r' % args)
    ...:
Link numver 0 points to url 'image1.html' and image 'image1_thumb.jpg'
Link numver 1 points to url 'image2.html' and image 'image2_thumb.jpg'
Link numver 2 points to url 'image3.html' and image 'image3_thumb.jpg'
Link numver 3 points to url 'image4.html' and image 'image4_thumb.jpg'
Link numver 4 points to url 'image5.html' and image 'image5_thumb.jpg'

1.5 选择元素属性

有多种方式获取属性的值。

XPath 选择属性

首先,可以使用 XPath 语法:

In [31]: response.xpath('//a/@href').getall()
Out[31]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

XPath 语法有多种优势:在标准的 XPath 格式中,@attributes 可以作为 XPath 表达式的一部分,等等,可以通过属性值来筛选内容。

CSS 选择属性

Scrapy 也提供了 CSS 选择器的扩展(::attr(...)),它允许这样获得属性值:

In [32]: response.css('a::attr(href)').getall()
Out[32]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

Selector attrib 属性

除了这种做法,还有 Selector 的 .attrib 属性。
如果你更希望使用 Python 代码,而不是通过 XPath 或者 CSS 扩展来获取属性,那么这种方式也比较有用:

In [33]: [a.attrib['href'] for a in response.css('a')]
Out[33]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

这种 python 代码直接读取的方式也可作用于 SelectorList 上;
将会将返回字典,包含第一个匹配元素的属性和值。
如果只需要选择器返回一个结果,那么这么做很方便:

In [34]: response.css('base').attrib
Out[34]: {'href': 'http://example.com/'}

In [35]: response.css('base').attrib['href']
Out[35]: 'http://example.com/'

1.6 在选择器中使用正则表达式

Selector 类还有 .re() 方法用于提取正则匹配的数据。
然而,不同于 .xpath() 和 .css() 方法,.re() 返回的是一个字符串列表。
所以 .re() 不能做内嵌的 .re() 调用。

提取图片名字的例子

In [36]: response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
Out[36]: ['My image 1 ', 'My image 2 ', 'My image 3 ', 'My image 4 ', 'My image 5 ']

In [37]: response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)')
Out[37]: 'My image 1 '

二、XPaths 的使用

在 Scrapy 中高效使用 XPath 的一些建议

扩展链接: http://www.zvon.org/comp/r/tut-XPath_1.html
扩展链接2:https://blog.scrapinghub.com/2014/07/17/xpath-tips-from-the-web-scraping-trenches/

2.1 使用 XPath 相对路径

假设你在嵌套选择器中使用 XPath,如果路径以 '/' 开头,那么 Xpath 会指向文档的绝对位置,而不是你现在调用的选择器。

比如,如果想提取 <div>元素 中的所有 <a> 元素。

首先,获取 <div> 元素,然后遍历 dvi 内容时使用 '//a' 定位元素

In [38]: divs = response.xpath('//div')

In [39]: for p in divs.xpath('//a'):
    ...:     print(p.get())
    ...:

结果返回为空。

实际上,第39行代码中的路径,意味着重新以文档起点为基准构建path,而不是在 <div> 元素中构建。

可以做这样的修改:

In [56]: for p in divs.xpath('.//a'):
    ...:     print(p)
    ...:
<Selector xpath='.//a' data='<a href="image1.html">Name: My image 1 <'>
<Selector xpath='.//a' data='<a href="image2.html">Name: My image 2 <'>
<Selector xpath='.//a' data='<a href="image3.html">Name: My image 3 <'>
<Selector xpath='.//a' data='<a href="image4.html">Name: My image 4 <'>
<Selector xpath='.//a' data='<a href="image5.html">Name: My image 5 <'>

更常规的使用方法,直接使用 a 即可:

In [56]: for p in divs.xpath('a'):
    ...:     print(p)
    ...:

2.2 当通过类 class 查询时,尽量考虑 CSS

由于一个元素可以包含多个 CSS classes,通过 XPath 锁定 class 的方式去选择元素会冗长无比。

你需要这样操作 contains(@class, 'someclass') 来弥补多个类的情况。

事实证明,Scrapy 选择器允许你使用「链选择器」,所以大多数情况下可以采用:先使用 CSS 筛选class,然后再切换到 XPath 继续筛选。

>>> sel = Selector(text='<div class="hero shout"><time datetime="2014-07-23 19:00">')
>>> sel.css('.shout').xpath('./time/@datetime').get()
'2014-07-23 19:00'

这种灵活使用的「链式筛选」非常实用,别忘了在使用 .xpath() 的时候,路径添加 '.'。

2.3 区别 //node[1] 和 (//node)[1]

//node[1] 返回的是所有父节点下,第一次出现的节点
(//node)[1] 返回所有文档匹配节点的第一个,只返回一个值

2.4 在条件中使用文本节点 ❤️

获取文本内容最好选择 XPath https://www.w3.org/TR/xpath/all/#section-String-Functions,而不要使用 .//text() 而选择 '.' 。

这是因为 .//text() 表达式会生成一个节点集合,当 节点集合(node-set) 转换成字符串,这个过程经常发生传递参数到字符串函数中,如 conatins() 或 starts_with() 函数,这会导致只会处理第一个元素。

>>> sel = Selector(text='<a href="#">Click here to go to the <strong>Next Page</strong>')
>>> sel.xpath('//a//text()').getall()
['Click here to go to the ', 'Next Page']
>>> sel.xpath("string(//a[1]//text())").getall() # convert it to string
['Click here to go to the ']

这里只返回了列表中的第一个元素

处理方式:将整个节点转换为字符串,这样就会把字内容都加在一起

>>> sel.xpath('//a[1]').getall()
['<a href="#">Click here to go to the <strong>Next Page</strong></a>']
>>> sel.xpath('string(//a[1])').getall()
['Click here to go to the Next Page']

从子节点中匹配字符串

如:

>>> sel.xpath("//a[contains(.//text(), 'Next Page')]").getall()
[]

使用 '.' ,情况变得不同

>>> sel.xpath("//a[contains(., 'Next Page')]").getall()
['<a href="#">Click here to go to the <strong>Next Page</strong></a>']

2.5 XPath 中的表达式

XPath 允许你在 XPath 表达式中引用变量,语法:$somevariable。
这有点类似于参数查询或 SQL 中的查询占位符 '?',意味着你可以预制查询点,再设置值。

举例一:不通过硬编码的方式,通过 id 属性来匹配元素

In [66]: response.xpath('//div[@id=$val]/a/text()', val='images').get()
Out[66]: 'Name: My image 1 '

举例二:找到包含五个<a>子标签的<div>标签的id值

In [69]: response.xpath('//div[count(a)=$cnt]/@id', cnt=5).get()
Out[69]: 'images'

在调用 .xpath() 的时候,必须绑定引用变量的值,否值会抛出 'ValueError: XPath error' 错误。

如果想了解更多,这部分内容是以 parsel 库为基础,更多详情和例子在 parse 教程中有介绍

2.6 移除域名

在使用爬虫项目时,经常会使用到「清除域名,只处理元素名」的操作,从而获得更简单/方便的 XPath。
你可以使用 Selector.remove_namespaces() 方法来移除域名。

首先,找到一个博客网站用于实践

(base) ☁  cherry  scrapy shell https://feeds.feedburner.com/PythonInsider

如果网页设定了命名空间,一旦我们想要尝试获取某些标签对象,会全部失败,返回None,因为 Atom XML 会混淆这些节点标签。

但是如果先调用了 Selector.remove_namespaces() 方法,那么就可以直接获取这些名称。

如果你在好奇为什么域名空间移除操作为什么不默认执行,而是采取手动的方式。简单来说有两个原因:

  1. 移除域名空间需要遍历和修改文档中所有节点,这意味着在Scrapy对所有文档执行默认抓取操作过程中,不可避免的产生高昂的操作量。
  2. 预防在某些情况下不需要使用域名空间,尽管这个操作很少见。

2.7 使用 EXSLT 扩展 ❤️

由于基于 lxml 顶层再封装,所以 Scrapy 也支持一些 EXSLT 扩展。
它可以预注册域名空间从而使用 XPath 表达式。

预置 域名 使用方式
re http://exslt.org/regular-expression http://exslt.org/regexp/index.html
set http://exsl.org/sets http://exslt.org/set/index.html

2.7.1 正则表达式

在 test() 方法中,可以提供许多 XPath 没法提供的方法

doc = u"""
<div>
    <ul>
        <li class="item-0"><a href="link1.html">first item</a></li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-inactive"><a href="link3.html">third item</a></li>
        <li class="item-1"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a></li>
    </ul>
</div>
"""
>>> sel = Selector(text=doc, type='html')
>>> sel.xpath('//li//@href').getall()
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
>>> sel.xpath('//li[re:test(@class, "item-\d$")]//@href').getall()
['link1.html', 'link2.html', 'link4.html', 'link5.html']

这里的 li[re:test(@class, "item-\d$')] 使用了正则表达式,这前面的 re() 处理节点文本不同,是处理属性值的正则表达式,而这个功能是 XPath 提供的 contains() 和 start-with() 进阶版本,它可以实现更强的匹配功能。

注意 🧡
C 语言库的 libxslt 不支持本地 EXSLT 正则表达式,所以 lxml 的功能实现与 Python 的 re 挂钩。尽管如此,在 XPath 中使用正则表达式函数会对性能有影响。

2.7.2 set 操作

set 操作可以在抓取文本前很方便地排除部分文档树内容。

比如从某个网站提取微型结构数据:网址(http://schema.org/Product)。只提取 timescopes 和与 itemprops 有关的组。

props = scop.xpath('''set:difference(./descendant::*/@itemprop, .//*[@itemscope]/*/@itemprop)''')

2.8 其他的 XPath 扩展

has-class 扩展

<p class="foo bar-baz">First</p>
<p class="foo">Second</p>
<p class="bar">Third</p>
<p>Fourth</p>

可以这么使用

>>> response.xpath('//p[has-class("foo")]')
[<Selector xpath='//p[has-class("foo")]' data='<p class="foo bar-baz">First</p>'>,
 <Selector xpath='//p[has-class("foo")]' data='<p class="foo">Second</p>'>]
>>> response.xpath('//p[has-class("foo", "bar-baz")]')
[<Selector xpath='//p[has-class("foo", "bar-baz")]' data='<p class="foo bar-baz">First</p>'>]
>>> response.xpath('//p[has-class("foo", "bar")]')
[]

所以 XPath 路径 '//p[has-class("foo", "bar-baz")]' 大致等同于 CSS 路径 'p.foo.bar-baz'。

但是记住,XPath 的方式会更慢,因为他是纯 Python 函数,这种方式仅适用于 CSS 选择器无法正常描述的场景。

parsel 的简单扩展

parsel.xpathfuncs.set_xpathfunc(fname, func)

三、引用内建的选择器

3.1 Selector 选择器对象

class scrapy.selector.Selector(response=None, text=None, type=None, root=None, **kwargs)

Selector 对象是选择部分响应内容的包装。

response

是 HtmlResponse 或 XMLResponse 对象,用于选择和抓取数据

text

uincode 字符串或utf-8编码的文本,在 response 不可用的情况下替代。不会发生使用 text 或 reponse 。

type

定义了选择器的类型,可以为 html/xml/None(默认)

  1. 如果 type 为 None,那么选择器基于响应,会选择最好的类型解析内容;如果内容为文本,则默认为 html。
  2. 如果 type 为 None,且传入 response,那么选择器的类型将会按照下面类型推断:
    • HtmlResponse 对应类型为 html
    • XmlResponse 对应选择器类型为 xml
    • 否则选择器类型为 html
  3. 如果 type 设定了类型,则不会对内容格式进行检测,强制使用设置的类型。

方法一 xpath

xpath(query, namespace=None, **kwargs)

寻找匹配 xpath 查询的节点,返回 SelectorList 选择器列表实例,扁平化所有的元素。
元素列表也实现了 Selector 的接口。

  • query

XPath 查询体,字符串

  • namaspace
    可选项

通过 register_namespace(prefix, uri) 注册后的内容,可以通过 prefis: namespace-uri 的形式使用,不过仅限于当前 Selector 调用。
TODO:这种方式的使用,有一点了解,但是不是很透彻,可待来日解决

任何新增的参数名称都可以通过 XPath 表达式传输给 XPath 变量

为方便起见,这个方法可以被 response.xpath() 调用

方法二 css

css(query)

应用给定的 CSS 选择器,返回 SelectorList 实例

  • query

CSS 选择器,字符串格式

在后台,CSS 查询会被解析成 XPath,并执行 .xpath() 方法

方法三 get

get()

加载并返回匹配的节点

attrib

返回基础元素的属性字典

re

re(regex, replace_entities=True)

应用给定的正则表达式,返回匹配的 unicode 字符串列表

  • regex
    可以是编译的正则表达式,字符串会通过 re.compile(regex) 编译。

默认情况下,如 ‘&' 会被直接解析成对应的内容;replace_entities 为 False 则不会做这种改变。

re_first

re_first(regex, default=None, replace_entities=True)

应用给定的正则表达式,返回第一个被匹配的 unicode 字符串。
如果没有匹配,返回默认的值(如果默认值没有提供,则默认为None)

另一个可选参数,如上

  • register_namespace(prefix, uri)
    注册给定的域名空间,在当前 Selector 中应用。
    没有注册域名空间,你无法从非标准的命名空间中选择或提取数据,具体查看下文 Selector examples on XML response

remove_namespaces()

移除所有的域名,允许直接跨过文档获取无域名空间的路径。

_bool_()

如果没有任何内容,返回 True,否则返回 False。
换句话说,选择器是否有内容

getall()

加载并返回匹配的所有节点,以 unicode 字符串列表展示。

3.2 选择器列表对象 SelectorList objects

class scrap.selector.SelectorList

SelectorList 对象是 list 子类,提供一些额外的方法

xpath(xpath, namespaces=None, **kwargs)

每个元素都调用 .xpath() 方法,并扁平化返回另一个 SelectorList 对象

  • query
  • namespace

css(query)

为每个元素调用 .css() 方法

  • query

getall()

为每个元素调用 .get() 方法

get(default=None)

返回第一个元素调用 .get() 的结果。
如果列表为空,则返回默认的结果

re(regex, replace_entities=True)

为每个元素调用 .re() 方法,扁平化返回列表

re_first(regex, default=None, replace_entities=True)

attrib

四、例子

选择 XML 响应

抓取网址:https://support.google.com/merchants/answer/160589?hl=en&ref_topic=2473799

提取所有的价格,需要注册一个域名空间:

sel = Selector(xml_reponse)

# 注册域名空间
sel.regisgter_namespace("g", "http://base.google.com/ns/1.0")
sel.xpath("//g:price").getall()

[1] https://docs.scrapy.org/en/latest/topics/selectors.html

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