python网络爬虫学习笔记(5)静态网页抓取(二)实践

文章目录

    • 1 资料
    • 2 笔记
      • 2-1 准备
      • 2-2 爬虫:豆瓣读书TOP250的所有图书的名称
        • 2-2-1 源码及结果
        • 2-2-2 完成爬虫的艰难探索

1 资料

  • 《Python网络爬虫从入门到实践》唐松,陈志铨。主要面向windows平台下的python3。
  • 百度知道
  • 可怜(误)的豆瓣读书
  • 其它见注脚

2 笔记

2-1 准备

书中推荐的实践项目如下,我首先打算做一个类似的项目 。

本章实践项目的目的是获取豆瓣电影TOP250的所有电影的名称,网页地址为:https://movie.douban.com/top250。在此爬虫中,将请求头定制为实际浏览器的请求头。

  1. 判断静态与否
    实践第一步,找目标。而这首先要找到一个大概率是静态网页才好继续下一步,关于这个问题百度之后发现1:(自带引用格式看起来太难受,我改成代码块了)
动态网页是与静态网页相对应的,也就是说,网页URL的后缀不是.htm、.html、.shtml、.xml等静态网页的常见形式,
而是以.asp、.jsp、.php、.perl、.cgi等形式为后缀,并且在动态网页网址中有一个标志性的符号——“?”。
  1. robots.txt
    确认了目标静态与否,找目标的下一步就是看robots.txt了(然而不少网站没有这个,总之试试吧)。这次先拿豆瓣读书试试:
User-agent: *
Disallow: /subject_search
Disallow: /search
Disallow: /new_subject
Disallow: /service/iframe
Disallow: /j/
Sitemap: http://www.douban.com/sitemap_index.xml
Sitemap: http://www.douban.com/sitemap_updated_index.xml

User-agent: Wandoujia Spider
Disallow: /

  1. 我已经看不太懂了……User-agent: *和Disallow:还好,但Disallow:后面的还有Sitemap: 都不明白呢。
  • Disallow:
    回顾笔记(1),总算明白Disallow:后面那一长串的意思了:
    Disallow: /abc 表示禁止访问收录abc.php、abc.html和abc文件夹下的所有文件。 Disallow: /abc/ 表示仅禁止访问abc文件夹下的所有文件,但是不限制abc.php、abc.html文件。
    也就是说Disallow: /subject_search表示禁止访问收录在subject_search.php、subject_search.html和subject_search文件夹下的所有文件。
    看起来这次第一个目标https://book.douban.com/top250?icn=index-book250-all(豆瓣读书TOP250)并没有限制这些,那倒是可以放心地访问了。(但它好像是个动态网页,真得没关系吗?)
  • Sitemap:
    Sitemap 可方便网站管理员通知搜索引擎他们网站上有哪些可供抓取的网页。最简单的 Sitemap 形式,就是XML 文件,在其中列出网站中的网址以及关于每个网址的其他元数据(上次更新的时间、更改的频率以及相对于网站上其他网址的重要程度为何等),以便搜索引擎可以更加智能地抓取网站。
    打开豆瓣读书的第一个Sitemap,其中一小段为:
<sitemap>
<loc>https://www.douban.com/sitemap17.xml.gzloc>
<lastmod>2019-08-31T20:56:17Zlastmod>
sitemap>
  • Sitemap:续
    其中< lastmod >是该链接的最后更新时间, < loc > (为识别出来加了空格)描述了具体的链接地址,打开可以获得一个压缩包,下载解压再打开可以看到具体的网页,取其中一段(changefreq可能会出现的更新频率;priority此链接相对于其他链接的优先权比值,值定于0.0 - 1.0之间):
- <url>
  <loc>https://music.douban.com/subject/1858957/loc> 
  <priority>0.000631481287316priority> 
  <changefreq>monthlychangefreq> 
  url>

显然要从这一大堆文件里找出我们的目标暂时很麻烦。在有能力对这一长串东西爬取之前,还是只能先相信书了(可以爬取豆瓣电影TOP250)。

2-2 爬虫:豆瓣读书TOP250的所有图书的名称

2-2-1 源码及结果

2-2-2是编写的经历,涉及soup.a.text.strip()(或者豆瓣电影top250源码里的soup.a.scan.text.strip())为什么改写成 i.a.text.replace(’\n’, ‘’)的解释。
源码:

import requests
from bs4 import BeautifulSoup


def sample_get_spider(link, headers, timeout = 20):
    """

    :param link: 爬取网站的url
    :param headers: 爬取时使用的请求头
    :param timeout: 爬取时间限制,默认20(单位:秒)
    :return: response响应对象
    """
    req = requests.get(link, headers=headers, timeout=timeout)
    print('页面响应码{0}'.format(req.status_code))
    return req


def touban_book_top250_spider():
    book_list = list()
    for i in range(0, 10):
        link = 'https://book.douban.com/top250?start=' + str(i * 25)
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/'
                                 '537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
                   'Host': 'book.douban.com'}
        req = sample_get_spider(link, headers)
        soup = BeautifulSoup(req.text, "lxml")  # 使用BeautifulSoup解析这段代码
        div_list = soup.find_all("div", class_="pl2")
        for i in div_list:
            book = i.a.text.replace('\n', '')
            book_list.append(book.replace(' ', ''))
    return book_list


def main():
    book_list = touban_book_top250_spider()
    print(book_list)


if __name__ == '__main__':
    main()

结果

页面响应码200
页面响应码200
页面响应码200
页面响应码200
页面响应码200
页面响应码200
页面响应码200
页面响应码200
页面响应码200
页面响应码200
# 这是手动输入的说明,测试结果中原本是一堆书名 ,出于**,用该说明代替#

2-2-2 完成爬虫的艰难探索

实际上到目前为止,某书还没怎么介绍获取标题时用到的BeautifulSoup,因为后面也许会有介绍(他没写的话只好自己弄了)这里只是运用笔记(3)中涉及的例子,相关代码如下:

from bs4 import BeautifulSoup
……
soup_1 = BeautifulSoup(req_1.text, "lxml") #使用BeautifulSoup解析这段代码
title_1 = soup_1.find("h1", class_ = "post-title").a.text.strip()

根据笔记(3)中积累的经验,先普普通通的看看网页源码 ,这里放出前两名图书的代码片段(修改了缩进):

<a class="nbg" href="https://book.douban.com/subject/1770782/"
  onclick="moreurl(this,{i:'0'})"
……
<img src="https://img3.doubanio.com/view/subject/m/public/s1727290.jpg" width="90" />
……
<div class="pl2">
<a href="https://book.douban.com/subject/1770782/"
 onclick=&#34;moreurl(this,{i:'0'})" title="追风筝的人"
>
……
……
……
<a class="nbg" href="https://book.douban.com/subject/25862578/"
  onclick="moreurl(this,{i:'1'})"
……
<img src="https://img3.doubanio.com/view/subject/m/public/s27264181.jpg" width="90" />
……
<div class="pl2">
<a href="https://book.douban.com/subject/25862578/"
 onclick=&#34;moreurl(this,{i:'1'})" title="解忧杂货店"
>

而笔记(3)对应的是(同样改了缩进)

   <h1 class="post-title"><a href="http://www.santostang.com/2018/07/15/4-3-%e9%80%9a%e8%bf%87
   selenium%e6%a8%a1%e6%8b%9f%e6%b5%8f%e8%a7%88%e5%99%a8%e6%8a%93%e5%8f%96/"
   >第四章 &#8211; 4.3 通过selenium 模拟浏览器抓取

这次的title在<>里面,不知道有没有影响,总之先试试。
结果果然炸了:

title = soup.find("div", class_="pl2").a.text.strip()
AttributeError: 'NoneType' object has no attribute 'a'

于是参观了答案(虽然是电影的):

div_list = soup.find_all('div', class_= 'hd')
for each in div_list:
    movie = each.a.span.text.strip()
    movie_list.append(movie)

改成book以后还是出错:

book = i.a.span.text.strip()
AttributeError: 'NoneType' object has no attribute 'text'

但是div_list可以打印出来,以其中一项为例:

<div class="pl2">
<a href="https://book.douban.com/subject/1770782/" onclick=""moreurl(this,{i:'0'})"" title="# 原本是书名 #">
                # 原本是书名 #

                
              </a>



                  <img alt="可试读" src="https://img3.doubanio.com/pics/read.gif" title="可试读"/>
<br/>
<span style="font-size:12px;">The Kite Runner</span>
</div>

而电影代码却可以正常使用,于是我决定查看豆瓣电影top250的代码:

<div class="info">
    <div class="hd">
         <a href="https://movie.douban.com/subject/1292052/" class="">
              <span class="title"># 原本是电影名 #
                      <span class="title">&nbsp;/&nbsp;The Shawshank Redemption</span>
                  <span class="other">&nbsp;/&nbsp;# 原本是其他地区的电影译名 #

span类……a.span原来是指这个……
将i.a.scan.text.strip()改为i.a.text.strip()后
得到了这样的结果:

# 这里本来应该是25个书名,并且有些书名之间存在大量“\n”和空格 #

这个结果并不是那么令人满意,如果只有25个是因为我只爬取了第一页,而大量“\n”和空格就有点奇怪了。
于是我又看了一遍网页源码,发现有大量“\n”和空格的条目确实有些不同:

<a href="https://book.douban.com/subject/1084336/" onclick=""moreurl(this,{i:'2'})"" title="# 原本是书名 #">
                # 原本是书名 #

                
              </a>
              
<a href="https://book.douban.com/subject/2567698/" onclick=""moreurl(this,{i:'6'})"" title="# 原本是书名 #
">
                # 原本是书名 #


                
                  <span style="font-size:12px;"> : # 原本是书的说明 # 
</a>

没错,这些条目在< a href >和< /a >之间多了一条

通过这一点,不难猜出i.a.text.strip()的具体含义:

book = i.a.text.replace('\n', '')
            book_list.append(book.replace(' ', ''))

最后提一下爬取全部十页的方法。虽然第一页叫https://book.douban.com/top250?icn=index-book250-all,后面的看起来才遵循一定规律(https://book.douban.com/top250?start=【i*25】,i在(0,10)之间),实际上也可以用这个规律来套第一页,也就是i取0(https://book.douban.com/top250?start=0)。


  1. 作者:naobing111
    链接:https://zhidao.baidu.com/question/228717377.html ↩︎

你可能感兴趣的:(python,爬虫)