一.爬虫基础

1.1 requests类

  1.1.1 request的7个方法

requests.request() 实例化一个对象,拥有以下方法

requests.get(url, *args)

requests.head() 头信息

requests.post()

requests.put()

requests.patch() 修改一部分内容

requests.delete()

  1. url = "http://quanben5.com/n/doupocangqiong/6.html"
  2.  
  3. headers = {
  4. "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36"
    }
  5.  
  6. r = requests.get(url, headers=headers)
  7.  
  8. data = {
  9. "pinyin": "doupocangqiong",
  10. "content_id": "",
  11. }
  12.  
  13. r = requests.post(url, data=data, headers=headers)

  1.1.2*arg里面的参数

params 字典或者字节序列,作为参数增加到url中
data 字典字节序列文件对象, 放在url里面对应的地方
json 作为requests的内容
headers 字典 模拟服务头
cookies 字典 cookieJar
auth 元组
files 字典类型,传输文件
timeout 设定的超时时间
proxies 字典类型,设定访问代理服务器,可以增加登录认证 pxs={"http":"http://user:pass@10.10.1.1234"}
allow_redirects 默认True 是否允许重定向
stream 默认TRUE 获得数据立刻下载
verity 默认True 认证SSL证书开关
cert 本地SSL证书路径

1.2 BeautifulSoup类

  1. soup = BeautifulSoup("","html.parser")
  1. soup.prettify()
  1. soup.find_all(name, attrs, recursive, string, **kwargs)
  1. name: 标签名称
    attrs: 属性
    string: 检索字符串
    soup.head
  1. soup.head.contents 列表
    soup.body.contents[1]
    soup.body.children
    soup.body.descendants
    .parent
  1. .parents
    .next_sibling 下一个
    .previous_sibling 上一个
    .next_siblings 下一个所有 迭代
    .previous_siblings 上一个所有

1.3 selenium

  1. from selenium import webdriver
  2. from selenium.webdriver.chrome.options import Options
  3. import time
  4.  
  5. chrome_options = Options()
  6. chrome_options.add_argument('--headless')
  7. chrome_options.add_argument('--disable-gpu')
  8.  
  9. d = webdriver.Chrome(chrome_options=chrome_options) # 设置成不显示浏览器的模式
  10. d.get('http://quanben5.com/n/dazhuzai/23241.html')
  11. time.sleep(2)
  12. print(d.page_source) # 显示出的是加载完之后的内容

查找单个和多个元素

d.find_element_by_id

d.find_elements_by_id

元素交互

  1. from selenium import webdriver
  2.  
  3. d = webdriver.Chrome()
  4. d.get('http://www.baidu.com')
  5. inputText = d.find_element_by_id("kw")
  6. inputText.send_keys("萝莉")
  7. button = d.find_element_by_id("su")
  8. button.click()

二.实战

首先要找到可以在线看小说的网页

这里我随便百度了一下,首先选择了一个全本5200小说网("https://www.qb5200.tw")

打开某小说章节目录表

https://www.qb5200.tw/xiaoshuo/0/357/

查看源代码

发现正文卷是在class为listmain的div下面的第二个dt标签里

之后的路径标签为a

  1. url_text = soup.find_all('div', 'listmain')[0] # 找到第一个class=listmain的div标签
  2. main_text = url_text.find_all('dt')[1] # 找到下面的第二个dt标签
  3. for tag in main_text.next_siblings:
  4. try:
  5. url = ''.join(['https://', host, tag.a.attrs['href']])
  6. print('parsering', url)
  7. except:
  8. continue

找到每个章节的url

在随便打开一章

查看源代码为:

  1. def getHTMLText(url, headers={}):
  2. """
  3. 获取网站源码
  4. :param url:
  5. :return: class response
  6. """
  7. if headers != {}:
  8. headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36"}
  9.  
  10. # proxies = get_random_ip()
  11. proxies = {}
  12. try:
  13. # print("start", url)
  14. r = requests.get(url, proxies=proxies, headers=headers)
  15. r.raise_for_status()
  16. # r.encoding = r.apparent_encoding
  17. # print('end', url)
  18. return r
  19. except:
  20. return r.status_code
  21.  
  22. def parseByQB5200(soup, host, f):
  23. """
  24. 在全本小说网的源码下爬取小说
  25. :param soup, host:
  26. :param f:
  27. :return:
  28. """
  29. url_text = soup.find_all('div', 'listmain')[0]
  30. main_text = url_text.find_all('dt')[1]
  31. x = 0
  32. for tag in main_text.next_siblings:
  33. time.sleep(1)
  34. try:
  35. url = ''.join(['https://', host, tag.a.attrs['href']])
  36. print('parsering', url)
  37. soup = BeautifulSoup(getHTMLText(url).text, "html.parser")
  38. passage = soup.find_all("div", "content")[0]
  39. title = passage.h1.string
  40. f.writelines(''.join([title, '\n']))
  41. passage = soup.find_all("div", "showtxt")[0]
  42. for i in passage.descendants:
  43. if i.name != "br":
  44. st = i.string
  45. if st.strip().startswith('http') or st.strip().startswith('请记住本书'):
  46. continue
  47. f.writelines(''.join([' ', st.strip(), '\n']))
  48. x += 1
  49. print('%d.%s 下载完成' % (x, title))
  50. except:
  51. continue
  52.  
  53. def getNovelUrls(url):
  54. """
  55. 通过小说的目录网址判断小说所在的网站
  56. 并调用属于该网站的爬虫语句
  57. :param url:
  58. :return:
  59. """
  60.  
  61. response = getHTMLText(url)
  62. host = url.split('//')[1].split('/')[0]
  63. host_list = {
  64. "www.qb5200.tw": parseByQB5200,
  65. # "www.qu.la": parseByQuLa,
  66. "quanben5.com": parseByQB5
  67. }
  68. print(response)
  69. soup = BeautifulSoup(response.text, 'html.parser')
  70. with open('1.txt', 'w', encoding='utf8') as f:
  71. host_list[host](soup, host, f)
  72.  
  73. if __name__ == '__main__':
  74. getNovelUrls("https://www.qb5200.tw/xiaoshuo/0/357/")

在全本5200爬取小说txt

问题在于

全本小说网("www.qb5200.tw")在这样的暴力获取下只允许爬3次,之后就403错误

本来以为是同一IP限制访问次数, 使用了IP代理之后发现问题依旧

猜测应该是跟请求头有关

因此加上了访问该网站时浏览器的所有请求头,并且把User-Agent设置为随机

  1.  
  1. USER_AGENT_LIST = [
  2. 'MSIE (MSIE 6.0; X11; Linux; i686) Opera 7.23',
  3. 'Opera/9.20 (Macintosh; Intel Mac OS X; U; en)',
  4. 'Opera/9.0 (Macintosh; PPC Mac OS X; U; en)',
  5. 'iTunes/9.0.3 (Macintosh; U; Intel Mac OS X 10_6_2; en-ca)',
  6. 'Mozilla/4.76 [en_jp] (X11; U; SunOS 5.8 sun4u)',
  7. 'iTunes/4.2 (Macintosh; U; PPC Mac OS X 10.2)',
  8. 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:5.0) Gecko/20100101 Firefox/5.0',
  9. 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0',
  10. 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20120813 Firefox/16.0',
  11. 'Mozilla/4.77 [en] (X11; I; IRIX;64 6.5 IP30)',
  12. 'Mozilla/4.8 [en] (X11; U; SunOS; 5.7 sun4u)'
  13. ]
  14. headers = {"User-Agent": random.choice(USER_AGENT_LIST),
  15. "Host": "www.qb5200.tw",
  16. "Connection": "keep-alive",
  17. "Pragma": "no-cache",
  18. "Cache-Control": "no-cache",
  19. "Upgrade-Insecure-Requests": "",
  20. "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
  21. "Referer": "https://www.qb5200.tw/xiaoshuo/0/355/",
  22. "Accept-Encoding": "gzip, deflate, br",
  23. "Accept-Language": "zh-CN,zh;q=0.9",
  24. "Cookie": "UM_distinctid=16736c47b1d8b-04bd85d2b5e8ab-50422618-144000-16736c47b1e1f2; bcolor=; font=; size=; fontcolor=; width=; CNZZDATA1260750615=1497982366-1542811927-%7C1542882690; fikker-7ZUK-qXIL=qDaKEBihatSNEFgtMlGVIKubCHYLi89J; fikker-7ZUK-qXIL=qDaKEBihatSNEFgtMlGVIKubCHYLi89J; fikker-Sbwr-GN9F=GbMX3HOhwvoDMxopLMGt3VWliXQIK0SP; fikker-Sbwr-GN9F=GbMX3HOhwvoDMxopLMGt3VWliXQIK0SP; fikker-yJ3O-W61D=UfinETMnCR38ADWZEl1KNHQRU2m81Fwb; fikker-yJ3O-W61D=UfinETMnCR38ADWZEl1KNHQRU2m81Fwb; fikker-rWK3-6KHs=T9T5b7lYVJReTQviPm2IdLPyHu83RwFM; fikker-rWK3-6KHs=T9T5b7lYVJReTQviPm2IdLPyHu83RwFM"
  25.  
  26. }

随机请求头

解决问题.

三.使用ajax动态加载的实例:

全本5小说网("quanben5.com")

同样的方法搜索源代码

然而发现了问题

给出的html页面只有一半的源码

因此按F12打开检查

发现所有的文本存在这个xhr里

点击查看请求头信息

发现是post请求

请求的url是/index.php?c=book&a=ajax_content

请求的数据在最下面的form表单里

打开网页源文件和js文件,搜索这些表单信息

分别在ajax.js里和源文件里找到了这些

源文件里面的可以直接生成data数据表单

在ajax.js里可以知道rndval字段是当前时间,精确到毫秒

四.优化

采用了gvent进行异步IO处理,每一张网页保存在temp里面,最后将文件合成一个txt

加入了搜索功能,目前仅支持一个小说网站

代码如下:

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3.  
  4. """
  5. @version:
  6. @author: Wish Chen
  7. @contact: 986859110@qq.com
  8. @file: get_novels.py
  9. @time: 2018/11/21 19:43
  10.  
  11. """
  12. import gevent
  13. from gevent import monkey
  14. monkey.patch_all()
  15. import requests, time, random, re, os
  16. from bs4 import BeautifulSoup
  17.  
  18. dir = os.path.dirname(os.path.abspath(__file__))
  19.  
  20. def getHTMLText(url, data=[], method='get'):
  21. """
  22. 获取网站的源代码
  23. 请求默认为get
  24. :param url:
  25. :param data:
  26. :param method:
  27. :return:
  28. """
  29. headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36"}
  30. proxies = {}
  31. try:
  32. # print("start", url)
  33. r = requests.request(method, url, proxies=proxies, headers=headers, data=data)
  34. r.raise_for_status()
  35. # r.encoding = r.apparent_encoding
  36. # print('end', url)
  37. return r
  38. except:
  39. return r.status_code
  40.  
  41. def fetch_async(x, url):
  42. """
  43. 异步IO所需要执行的操作
  44. 获取源文件
  45. 模拟向ajax请求获取完整文字
  46. 每一章输入到temp文件夹下
  47. :param x:
  48. :param url:
  49. :return:
  50. """
  51. url_main = "http://quanben5.com/index.php?c=book&a=ajax_content"
  52. r = getHTMLText(url) # 获取每一章的源文件
  53. title = re.search(r'<h1 class="title1">(.*)</h1>', r.text).group(1)
  54. result = re.search(r'<script type="text/javascript">ajax_post\((.*)\)</script>', r.text).group(1)
  55. num_list = result.split("','")
  56. num_list[9] = num_list[9][:-1]
  57. content = {} # 开始模拟post请求发送的表单
  58. for i in range(1, 5):
  59. content[num_list[i * 2]] = num_list[i * 2 + 1]
  60. content['_type'] = "ajax"
  61. content['rndval'] = int(time.time() * 1000)
  62. r = getHTMLText(url_main, data=content, method='post') # 模拟post请求
  63. soup = BeautifulSoup(r.text, "lxml")
  64. with open(os.path.join(dir, 'temp', "%s.txt" % x), "w", encoding='utf8') as f:
  65. f.writelines(''.join([str(x), '.', title, '\n\n']))
  66. for tag in soup.body.children:
  67. if tag.name == 'p':
  68. f.writelines(''.join([' ', tag.string.strip(), '\n\n']))
  69. print('%d.%s 下载完成' % (x, title))
  70.  
  71. def get_together(name, author, x):
  72. """
  73. 将temp目录下的各网页下载下来的txt
  74. 合并在一起
  75. 并删除temp文件
  76. :param name:
  77. :param author:
  78. :return:
  79. """
  80. with open(os.path.join(dir, "%s.txt" % name), "w", encoding='utf8') as f:
  81. f.writelines(''.join([name, '\n\n作者:', author, '\n\n']))
  82.  
  83. for i in range(x):
  84. try:
  85. f.write(open(os.path.join(dir, 'temp', "%s.txt" % (i+1)), "r", encoding='utf8').read())
  86. f.write('\n\n')
  87. # os.remove(os.path.join(dir, 'temp', "%s.txt" % (i+1)))
  88. except:
  89. continue
  90.  
  91. def parseByQB5(response, host):
  92. """
  93. 在全本5小说网的源码下爬取小说
  94. 获得书名和作者
  95. 采用gevent异步IO优化
  96. :param response:
  97. :param host:
  98. :return:
  99. """
  100. soup = BeautifulSoup(response.text, 'html.parser')
  101. url_text = soup.find_all('div', 'box')[2]
  102. main_text = url_text.find_all('h2')[0].next_sibling
  103. url_list = []
  104. for tag in main_text.descendants:
  105. if tag.name == 'li':
  106. url = ''.join(['http://', host, tag.a.attrs['href']])
  107. url_list.append(url)
  108. from gevent.pool import Pool
  109. pool = Pool(100)
  110.  
  111. gevent.joinall([pool.spawn(fetch_async, i+1, url=url_list[i]) for i in range(len(url_list))])
  112.  
  113. name = re.search(r"<h3><span>(.*)</span></h3>", response.text).group(1)
  114. author = re.search(r'<span class="author">(.*)</span></p>', response.text).group(1)
  115. print("%d文档已下载,正在合并..." % len(url_list))
  116. get_together(name, author, len(url_list))
  117.  
  118. def getNovelUrls(url):
  119. """
  120. 通过小说的目录网址判断小说所在的网站
  121. 并调用属于该网站的爬虫语句
  122. :param url:
  123. :return:
  124. """
  125.  
  126. response = getHTMLText(url)
  127. host = url.split('//')[1].split('/')[0]
  128. host_list = {
  129. "quanben5.com": parseByQB5
  130. }
  131. host_list[host](response, host)
  132.  
  133. def get_url():
  134. input_name = input('>>')
  135. r = getHTMLText("http://quanben5.com//index.php?c=book&a=search&keywords=%s" % input_name)
  136. soup = BeautifulSoup(r.text, "html.parser")
  137. main_book = soup.find_all("div", "pic_txt_list")
  138. for i in range(len(main_book)):
  139. tag = main_book[i].h3
  140. print("%s.%s %s" %(i, tag.span.text, tag.next_sibling.next_sibling.text))
  141. choice = int(input(">>"))
  142. if choice in range(len(main_book)):
  143. return ''.join(["http://quanben5.com", main_book[choice].h3.a["href"], "xiaoshuo.html"])
  144.  
  145. if __name__ == '__main__':
  146. # url_list = [
  147. # "https://www.qu.la/book/365/",
  148. # "https://www.qb5200.tw/xiaoshuo/0/357/",
  149. # "http://quanben5.com/n/doupocangqiong/xiaoshuo.html",
  150. # "http://quanben5.com/n/dazhuzai/xiaoshuo.html",
  151. # "http://quanben5.com/n/douluodalu/xiaoshuo.html",
  152. # "http://quanben5.com/n/renxingderuodian/xiaoshuo.html"
  153. # ]
  154. # if not os.path.exists('temp'):
  155. # os.mkdir('temp')
  156. # getNovelUrls(url_list[5])
  157. while True:
  158. url = get_url()
  159. time_start = time.time()
  160. getNovelUrls(url)
  161. print("成功爬取! 用时:%ds" % (int((time.time()-time_start)*100)/100))

异步IO+搜索

封装性还不够好

最好一个网站用一个类来封装

采用scrapy框架

正在设计中...

五.未解决问题:

代理IP问题:

目前只有免费的代理IP网

生成随机IP并使用代理IP访问

多网站定制

要观察各个网站的目录源代码结构以及文章源代码结构

每一个网站都可以用一个parse函数来解析其内容

python从爬虫基础到爬取网络小说实例的更多相关文章

  1. python爬虫-基础入门-爬取整个网站《3》

    python爬虫-基础入门-爬取整个网站<3> 描述: 前两章粗略的讲述了python2.python3爬取整个网站,这章节简单的记录一下python2.python3的区别 python ...

  2. python爬虫-基础入门-爬取整个网站《2》

    python爬虫-基础入门-爬取整个网站<2> 描述: 开场白已在<python爬虫-基础入门-爬取整个网站<1>>中描述过了,这里不在描述,只附上 python3 ...

  3. python爬虫-基础入门-爬取整个网站《1》

    python爬虫-基础入门-爬取整个网站<1> 描述: 使用环境:python2.7.15 ,开发工具:pycharm,现爬取一个网站页面(http://www.baidu.com)所有数 ...

  4. python3爬虫-使用requests爬取起点小说

    import requests from lxml import etree from urllib import parse import os, time def get_page_html(ur ...

  5. python爬虫基础应用----爬取校花网视频

    一.爬虫简单介绍 爬虫是什么? 爬虫是首先使用模拟浏览器访问网站获取数据,然后通过解析过滤获得有价值的信息,最后保存到到自己库中的程序. 爬虫程序包括哪些模块? python中的爬虫程序主要包括,re ...

  6. Python爬虫基础--分布式爬取贝壳网房屋信息(Client)

    1. client_code01 2. client_code02 3. 这个时候运行多个client就可以分布式进行数据爬取.

  7. python 爬取网络小说 清洗 并下载至txt文件

    什么是爬虫 网络爬虫,也叫网络蜘蛛(spider),是一种用来自动浏览万维网的网络机器人.其目的一般为编纂网络索引. 网络搜索引擎等站点通过爬虫软件更新自身的网站内容或其对其他网站的索引.网络爬虫可以 ...

  8. python简单爬虫 用beautifulsoup爬取百度百科词条

    目标:爬取“湖南大学”百科词条并处理数据 需要获取的数据: 源代码: <div class="basic-info cmn-clearfix"> <dl clas ...

  9. Python爬虫基础--分布式爬取贝壳网房屋信息(Server)

    1. server_code01 2. server_code02 3. server_code03

随机推荐

  1. 12-tinyMCE文本编辑器+图片上传预览+页面倒计时自动跳转

    文本编辑器插件:1.将tinymce文件夹全部复制到webContent下2.tinymce/js目录下放 jquery等三个js文件3.语言包:tinymce/js/tinymce/langs目录下 ...

  2. 三:OVS+GRE之完整网络流程

    知识点一:linux网桥提供安全组 知识点二:每新建一个网络,在网络节点都会新建一个namespace,只要为该网络建立子网,那么该namespace里就新增dhcp来为该子网分配ip,也可以为该网络 ...

  3. Luogu4755 Beautiful Pair 最值分治、主席树

    传送门 整天做一些模板题感觉药丸 设\(val_i\)表示第\(i\)个位置的值 看到区间最大值考虑最值分治.对于当前的区间\([l,r]\),找到区间最大值\(mid\),递归\([l,mid-1] ...

  4. 最简单易懂的Spring Security 身份认证流程讲解

    最简单易懂的Spring Security 身份认证流程讲解 导言 相信大伙对Spring Security这个框架又爱又恨,爱它的强大,恨它的繁琐,其实这是一个误区,Spring Security确 ...

  5. MySQL与MongoDB

    MySQL        MongoDB DB DB table Collections row  Documents column    Field 增 db.tables.insert({})#效 ...

  6. 第二部分之RDB持久化(第十章)

    RDB持久化功能所生成的RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态.(数据库状态:服务器中的非空数据库以及它们的键值对统称为数据库状态) 一.RDB文件的创建 ...

  7. 使用mongo-express管理mongodb数据库

    前面的话 本文将详细介绍一款用nodejs开发的基于Web的mongodb数据库管理工具mongo-express 安装 首先,全局安装 mongo-express 包 npm install -g ...

  8. SQL 中左连接与右链接的区别

    在微信公众号中看到的sql左连接与右链接的总结,这个图总结的很好,所以单独收藏下:

  9. 测试常用Linux命令

    大家应该经常在网络上看到下图吧,虽然我们不会去执行下面图片中的命令,但是linux常用的命令对于测试人员来说,还是必须掌握的,不管是做功能测试还是性能测试,最常用的就是看日志了. sudo是linux ...

  10. Matlab常用函数集锦

    ndims(A)返回A的维数size(A)返回A各个维的最大元素个数length(A)返回max(size(A))[m,n]=size(A)如果A是二维数组,返回行数和列数nnz(A)返回A中非0元素 ...