【第2周】网络爬虫之提取

单元4:Beautiful Soup库入门

Beautiful Soup库的安装

管理员身份运行命令提示符

pip install beautifulsoup4

Beautiful Soup库的安装小测
演示HTML页面网址:http://python123.io/ws/demo.html
查看源代码:

  • 浏览器页面右键→查看源代码
  • 使用requests库
>>> import requests
>>> r = requests.get("https://python123.io/ws/demo.html")
>>> r.text
'<html><head><title>This is a python demo page</title></head>\r\n<body>\r\n<p class="title"><b>The demo python introduces several python courses.</b></p>\r\n<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\r\n<a href="http://www.icourse163.org/course/BIT-268001" class="py1" id="link1">Basic Python</a> and <a href="http://www.icourse163.org/course/BIT-1001870001" class="py2" id="link2">Advanced Python</a>.</p>\r\n</body></html>'
>>> demo = r.text

测试beautiful soup库

>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(demo, "html.parser")

其中html.parser是解释器,对demo进行解析。

>>> print(soup.prettify())
<html>
 <head>
  <title>
   This is a python demo page
  </title>
 </head>
 <body>
  <p class="title">
   <b>
    The demo python introduces several python courses.
   </b>
  </p>
  <p class="course">
   Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:
   <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">
    Basic Python
   </a>
   and
   <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">
    Advanced Python
   </a>
   .
  </p>
 </body>
</html>

beautifulsoup成功解析demo。
使用beautifulsoup:

from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>data</p>', 'html.parser')

前一个参数为html类型的参数,后面是解释器

Beautiful Soup库的基本元素

Beautiful Soup库的理解

html:一堆“标签树”组成的
解析、遍历、维护“标签树”的功能库。
以其中一组“标签树”为例:

<p class = "title"> ... </p>

其中
<p> ... </p>:标签。成对出现。第一个是名称Name,属性Attributes,0个或多个,属性由键值对构成。
Beautiful Soup库的引用,常用为

from bs4 import BeautifulSoup

也可以直接

import bs4

Beautiful Soup类

HTML<->标签树<->BeautifulSoup类

Beautiful Soup库解析器

解析器 使用方法 条件
bs4的HTML解析器 BeautifulSoup(mk, 'html.parser') 安装bs4库
lxml的HTML解析器 BeautifulSoup(mk, 'lxml') pip install lxml
lxml的XML解析器 BeautifulSoup(mk, 'xml') pip install lxml
html5lib的解析器 BeautifulSoup(mk, 'html5lib') pip install html5lib

Beautiful Soup类的基本元素

基本元素 说明
Tag 标签,最基本的信息组织单元,分别用<>和</>表明开头和结尾
Name 标签的名字,<p>...</p>的名字是'p',格式:<tag>.name
Attributes 标签的属性,字典形式组织,格式:<tag>.attrs
NavigableString 标签内非属性字符串,<>...</>中字符串,格式<tag>.string
Comment 标签内字符串的注释部分,一种特殊的Comment类型

回顾demo.html
查看title标签

>>> from bs4 import BeautifulSoup
>>> import requests
>>> r = requests.get("https://python123.io/ws/demo.html")
>>> demo = r.text
>>> soup = BeautifulSoup(demo, "html.parser")
>>> soup.title
<title>This is a python demo page</title>

获取html中.a标签的内容

>>> tag = soup.a
>>> tag
<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>

任何标签都可以用这种方式获得。存在相同标签时,只返回第一个。
获取标签名字:

>>> soup.a.name
'a'
>>> soup.a.parent.name    #获取父亲名字
'p'
>>> soup.a.parent.parent.name
'body'

获取标签属性信息

>>> tag = soup.a
>>> tag.attrs
{'href': 'http://www.icourse163.org/course/BIT-268001', 'class': ['py1'], 'id': 'link1'}

属性是一个字典,可进行信息提取。

>>> tag.attrs['class']
['py1']
>>> tag.attrs['href']
'http://www.icourse163.org/course/BIT-268001'

标签属性的类型

>>> type(tag.attrs)
<class 'dict'>
>>> type(tag)
<class 'bs4.element.Tag'>

标签的NavigableString元素

>>> soup.a
<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>
>>> soup.a.string
'Basic Python'
>>> soup.p
<p class="title"><b>The demo python introduces several python courses.</b></p>
>>> soup.p.string
'The demo python introduces several python courses.'

其类型为

>>> type(soup.p.string)
<class 'bs4.element.NavigableString'>

NavigableString可跨多个标签层次
标签的Comment元素

>>> newsoup = BeautifulSoup("<b><!--This is a comment--></b><p>This is not a comment</p>", "html.parser")
>>> newsoup.b.string
'This is a comment'
>>> type(newsoup.b.string)
<class 'bs4.element.Comment'>
>>> newsoup.p.string
'This is not a comment'
>>> type(newsoup.p.string)
<class 'bs4.element.NavigableString'>

p标签是一个字符串,NavigableString;b标签是一个字符串,为Comment。

5种基本类型


BeautifulSoup库的理解

基于bs4库的HTML内容遍历方法

回顾demo.html:

>>> import requests
>>> r = requests.get("https://python123.io/ws/demo.html")
>>> r.text
'<html><head><title>This is a python demo page</title></head>\r\n<body>\r\n<p class="title"><b>The demo python introduces several python courses.</b></p>\r\n<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\r\n<a href="http://www.icourse163.org/course/BIT-268001" class="py1" id="link1">Basic Python</a> and <a href="http://www.icourse163.org/course/BIT-1001870001" class="py2" id="link2">Advanced Python</a>.</p>\r\n</body></html>'
>>> demo = r.text

HTML基本格式


HTML标签树

3种遍历方法:

  • 下行遍历
  • 上行遍历
  • 平行遍历

标签树的下行遍历

属性 说明
.contents 子节点的列表,列表类型,将<tag>所有儿子节点存入列表
.children 子节点的迭代类型,与.contents类似,用于循环遍历儿子节点
.descendants 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历

例如

>>> soup = BeautifulSoup(demo, "html.parser")
>>> soup.head
<head><title>This is a python demo page</title></head>
>>> soup.head.contents
[<title>This is a python demo page</title>]
>>> soup.body.contents
['\n', <p class="title"><b>The demo python introduces several python courses.</b></p>, '\n', <p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:

<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a> and <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>.</p>, '\n']
>>> len(soup.body.contents)
5
>>> soup.body.contents[1]
<p class="title"><b>The demo python introduces several python courses.</b></p>

遍历儿子节点:

for child in soup.body.children:
    print(child)

遍历子孙节点:

for child in soup.body.descendants:
    print(child)

上行遍历

属性 说明
.parent 节点的父亲标签
.parents 节点先辈标签的迭代类型,用于循环遍历先辈节点
>>> soup.title.parent
<head><title>This is a python demo page</title></head>
>>> soup.html.parent
<html><head><title>This is a python demo page</title></head>
<body>
<p class="title"><b>The demo python introduces several python courses.</b></p>
<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:

<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a> and <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>.</p>
</body></html>       #html先辈是其本身
>>> soup.parent
      #soup的先辈是空的

上行遍历:

>>> soup.parent
>>> for parent in soup.a.parents:
    if parent is None:
        print(parent)
    else:
        print(parent.name)

p
body
html
[document]

平行遍历

属性 说明
.next_sibling 返回按照HTML文本顺序的下一个平行节点标签
.previous_sibling 返回按照HTML文本顺序的上一个平行节点标签
.next_siblings 迭代类型,返回按照HTML文本顺序的后续所有平行节点标签
.previous_siblings 迭代类型,返回按照HTML文本顺序的前续所有平行节点标签

所有的平行遍历发生在同一个父节点下的各节点间。

>>> soup = BeautifulSoup(demo, "html.parser")
>>> soup.a.next_sibling
' and '

a标签的下一个平行标签是and。
平行遍历获得的下一个节点不一定是标签。

>>> soup.a.next_sibling.next_sibling
<a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>
>>> soup.a.previous_sibling
'Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\r\n'
>>> soup.a.previous_sibling.previous_sibling
>>> soup.a.parent
<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:

<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a> and <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>.</p>

标签树的循环平行遍历:
遍历后续节点

for sibling in soup.a.next_siblings:
    print(sibling)

遍历前序节点

for sibling in soup.a.previous_siblings:
    print(sibling)

基于bs4库的HTML格式化和编码

bs4的prettify()方法

>>> soup.prettify()
'<html>\n <head>\n  <title>\n   This is a python demo page\n  </title>\n </head>\n <body>\n  <p class="title">\n   <b>\n    The demo python introduces several python courses.\n   </b>\n  </p>\n  <p class="course">\n   Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\n   <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">\n    Basic Python\n   </a>\n   and\n   <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">\n    Advanced Python\n   </a>\n   .\n  </p>\n </body>\n</html>'
>>> print(soup.prettify())
<html>
 <head>
  <title>
   This is a python demo page
  </title>
 </head>
 <body>
  <p class="title">
   <b>
    The demo python introduces several python courses.
   </b>
  </p>
  <p class="course">
   Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:
   <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">
    Basic Python
   </a>
   and
   <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">
    Advanced Python
   </a>
   .
  </p>
 </body>
</html>

为html文本增加换行符。
也可为每个标签进行处理。

>>> print(soup.a.prettify())
<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">
 Basic Python
</a>

bs4库编码为utf-8编码。可以很好地支持中文编码。


三种遍历方法

单元5:信息组织与提取方法

信息标记的三种形式

对信息进行标记,理解信息的类型与真实含义。

  • 标记后的信息形成组织结构,增加信息维度
  • 标记后的信息可用于通信、存储或展示
  • 标记的结构与信息一样具有重要价值
  • 标记后的侵袭更利于程序理解和运用

HTML的信息标记

通过预定义的<>...</>标签形式组织不同类型的信息
信息标记的形式:XML,JSON和YAML。

  • XML:eXtensible Markup Language,用尖括号标签表示信息。
  • JSON:JavaScript Object Notation,有类型的键值对构成的形式。例如:"name" : "abc",多个值用逗号隔开[,],如"name" : [ "abc", "def" ]。键值对可嵌套,用{,},如:
"name" : {
    "newName" : "abc",
    "oldName" : "def"
         }
  • YAML:YAML Ain't Markup Language,无类型的键值对。例如:name : abc。多个嵌套,使用缩进:
name : 
    newName : abc
    oldName : def

表达并列关系:前面加上“-”,表达整块数据,用“|”,注释加上“#”

三种标记形式的比较

  • XML:有效信息比例不高,大部分信息被标签占用。最早的通用信息标记语言,扩展性很好,繁琐。Internet上的信息传递与交互。
  • JSON:需要用双引号表达类型。信息有类型,适合程序处理(js),较XML简洁。移动应用云端与节点的信息通信,无注释。
  • YAML:信息五类型,文本信息比例最高,可读性好。各类系统的配置文件,有注释易读。

信息提取的一般方法

  • 方法一:完整解析信息的标记形式,再提取关键信息。
    需要表及解析器,例如:bs4库的标签树遍历
    优点:信息解析准确。
    缺点:提取过程繁琐,速度慢。
  • 方法二:无视标记形式,直接搜索关键信息。
    对信息的文本查找函数即可。
    优点:提取过程简洁,速度较快。
    缺点:提取结果准确性与信息内容相关。
  • 方法三:融合方法。
    结合形式解析与搜索方法,提取关键信息。
    需要标记解析器及文本查找函数。

实例:
提取HTML中所有URL连接
思路:

  • 搜索所有<a>标签。
  • 解析<a>标签格式,提取href后的链接内容。
    例如,对前述的demo查找:
>>> for link in soup.find_all('a'):
    print(link.get('href'))

    
http://www.icourse163.org/course/BIT-268001
http://www.icourse163.org/course/BIT-1001870001

基于bs4库的HTML内容查找方法

<>.find_all(name, attrs, recursive, string, **kwargs)

返回一个列表类型,存储查找的结果。

  • name:对标签名称的检索字符串。
>>> soup.find_all('a')
[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>, <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>]

输出为列表类型。
同时查找a标签和b标签:

>>> soup.find_all(['a','b'])
[<b>The demo python introduces several python courses.</b>, <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>, <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>]

查找所有标签标签:

>>> for tag in soup.find_all(True):
    print(tag.name)

    
html
head
title
body
p
b
p
a
a

只显示b开头的标签:
(使用新库:正则表达式库)

>>> import re
>>> for tag in soup.find_all(re.compile('b')):
    print(tag.name)

    
body
b
  • attrs:对标签属性值的检索字符串,可标注属性检索。
    例如,带有course属性值的p标签:
>>> soup.find_all('p', 'course')
[<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:

<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a> and <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>.</p>]

查找属性中id域为link1的元素:

>>> soup.find_all(id='link1')
[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>]

查找属性时,必须精确复制信息,完整准确。如查找link就没有结果:

>>> soup.find_all(id='link')
[]

查找属性的部分信息:(需要正则表达式库),如包含link的标签信息。

>>> soup.find_all(id=re.compile('link'))
[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>, <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>]
  • recursive:是否对子孙全部搜索,默认为True。
>>> soup.find_all('a')
[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>, <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>]
>>> soup.find_all('a', recursive = False)
[]

结果说明,从soup开始的儿子节点没有a标签。

  • string:对<>...</>中字符串区域的检索字符串。
    必须要精确输入才能进行检索。如果需要检索包含的信息,使用正则 表达式库。
>>> soup
<html><head><title>This is a python demo page</title></head>
<body>
<p class="title"><b>The demo python introduces several python courses.</b></p>
<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:

<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a> and <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>.</p>
</body></html>
>>> soup.find_all(string = 'Basic Python')
['Basic Python']
>>> import re
>>> soup.find_all(string = re.compile("python"))
['This is a python demo page', 'The demo python introduces several python courses.']

简化形式:
<tag>(...)等价于<tag>.find_all(...)
soup(...)等价于soup.find_all(...)

扩展方法,与find_all()具有相同的参数。

方法 说明
<>.find() 搜索且只返回一个结果,字符串类型
<>.find_parents() 在先辈节点中搜索,返回列表类型
<>.find_parent() 在先辈节点中返回一个结果,字符串类型
<>.find_next_siblings() 在后续平行节点中搜索,返回列表类型
<>.find_next_sibling() 在后续平行节点中返回一个结果,字符串类型
<>.find_previous_siblings() 在前续平行节点中搜索,返回列表类型
<>.find_previous_sibling() 在前续平行节点中返回一个结果,字符串类型

“中国大学排名定向爬虫”实例

实例介绍

链接:http://www.zuihaodaxue.cn/zuihaodaxuepaiming2019.html

功能描述:
输入:大学排名URL链接
输出:大学排名信息的屏幕输出(排名,大学名称,总分)
技术路线:requests-bs4
定向爬虫:仅对输入的URL进行爬取

确定可行性:
确认返回的信息是否存在于html代码中。
打开浏览器,输入网站。
右键,查看源代码。快速搜索清华大学的信息。可以发现html代码中发现具体信息。
查看robots协议。http://www.zuihaodaxue.cn/robots.txt。页面不存在,可进行爬取。

步骤:

  • 第一步:从网络上获取大学排名网页内容。
    getHTMLText()
  • 第二步:提取网页内容中信息到合适的数据结构。
    fillUnivList()
  • 第三步:利用数据结构展示并输出结果。
    printUnivList()

数据结构:二维列表。

代码实现

import requests
from bs4 import BeautifulSoup
import bs4

def getHTMLText(url):  #从网络上获取大学排名网页内容
    try:
        r = requests.get(url, timeout = 30)  #获取信息,超时时间设定为30s
        r.raise_for_status()  #获取状态
        r.encoding = r.apprent_encoding  #改变编码方式
        return r.text
    except:
        return ""

def fillUnivList(ulist, html):  #提取网页内容中信息到合适的数据结构
    soup = BeautifulSoup(html, "html.parser")
    for tr in soup.find('tbody').children:  #观察html信息,所有信息都在tbody中的孩子的tr标签中
        if isinstance(tr, bs4.element.Tag):  #检测tr标签的类型,如果不是bs4库中的Tag类型,将进行过滤
            tds = tr('td')  #找出tr标签中tds
            ulist.append([tds[0].string, tds[1].string, tds[2].string])  #添加信息到ulist表中
    pass

def printUnivList(ulist, num):  #利用数据结构展示并输出结果
    print("{:^10}\t{:^6}\t{:^10}".format("排名", "学校名称", "总分"))  #打印表头
    for i in range(num):
        u = ulist[i]
        print("{:^10}\t{:^6}\t{:^10}".format(u[0], u[1], u[2]))
    print("Suc" + str(num))

def main():
    uinfo = []  #将大学信息放在一列表中
    url = 'http://www.zuihaodaxue.cn/zuihaodaxuepaiming2019.html'
    html = getHTMLText(url)   #将url中的内容转换为html格式
    fillUnivList(uinfo, html)
    printUnivList(uinfo, 20)  #打印前20所学校的信息

main()

注:现在网页源代码改变,运行会报错。

优化

已经实现了从网上获取相关信息并获取结果。中文字符对齐效果不好。
原因:format方法中相关格式。当中文字符宽度不够时,采用西文字符填充。
处理:采用中文字符额空格填充chr(12288)
printUnivList()中修改:

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

推荐阅读更多精彩内容