• 【爬虫】实验项目一:文本反爬网站的分析和爬取


    目录

    一、实验目的

    二、实验预习提示

    ​编辑

    三、实验内容

     四、实验要求

     五、实验过程

    1. 基本要求:

    2. 改进要求A

    3. 改进要求B:

    六、资料

    1.实验框架代码:

    2.OpenSSL:Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions (slproweb.com)

    3.Josn存储,先安装json包:

    4.实验小提示

    七、源码


    一、实验目的

            熟悉使用Selenium、Pyppeteer等工具爬取网站基本内容,通过分析具有文本反爬技术网站,设计爬取策略来获取文本正确的内容。


    二、实验预习提示

    • 安装Python环境 (Python 3.x):Pychram+Anaconda
    • 为Python安装Selenium、PyQuery库(打开pycharm新建项目,选择Anaconda创建的Python环境,在下面对应Console窗口执行):
    1. pip install selenium
    2. pip install pyquery
    • 安装Chrome和对应ChromeDriver:

            下载安装完后查看Chrome版本:点击 Chrome 的菜单,帮助 -> 关于 Chrome,即可查看 Chrome 的版本号105.0.5195.127,如图所示:

     在ChromeDriver 官方网站ChromeDriver - WebDriver for Chrome - Downloads (chromium.org)下载Chrome版本对应的驱动(105.0.5195.x, 看主版本号105都行),点击下划线的链接,根据系统型号下载。windows下chromedriver_win32.zip,其他系统找到对应版本下载:

    下面这部分配置环境变量内容【到图片结束(包含图片)】可以省略,参考最新内容【爬虫】5.2 Selenium编写爬虫程序_即使再小的船也能远航的博客-CSDN博客

     运行代码前配置系统环境变量Path前指定chrome driver位置:

    Path=替换为chrome driver解压后的位置

    或者在Pycharm运行配置指定:


    三、实验内容

    爬取网站:Scrape | Book

    使用浏览器开发者工具(F12),分析网站结构和其中文本反爬机制,编码实现获取该网站每本书的封面图片URL、书名和作者信息。实验基框架代码见文档末资料。


     四、实验要求

            基本要求将网站一页每本书的信息保存在一个josn文件中,每个json文件命名为:书名.json,其内容为保存书籍相应的信息:

    1. {
    2.   "title": "Wonder",
    3.   "cover_url":"https://img1.doubanio.com/view/subject/l/public/s27252687.jpg",
    4. "authors":"R. J. Palacio"
    5. }

            实现方法不一定要用Selenium、Pyppeteer,但是必须是Python编写的,并以完成实验要求为准,并附上代码运行结果。

            改进要求A在完成基本要求的基础上,选项一:实现可以遍历网站的每一页来爬取书籍信息。或指定爬取条目数量,当爬取总条目满足数量后停止爬取。选项二:或者举例至少三个其他网站的文本爬虫技术,分析并给出解决方案,不需要实现。

            改进要求B在完成改进要求A的选项一的基础上,可以爬取书籍的额外信息,如评分,出版时间,出版社,ISBM, 价格等。


     五、实验过程

    1. 基本要求:

            想要爬取网页内容,首先得分析网页结构,查看源代码如下图所示,

    • 点击封面有对应该书得二级页面(详情)后半部分地址(改进要求B用);
    • 书的封面URL可以用img.class查询;
    1. # 获取书籍封面图片url
    2. for tag in soup.select("img.cover"):
    3. pics.append(tag.attrs['src'])
    • 书名都在h3标题中,如果是英文书名,直接h3.name即可,但中文书名由多个class="char"的SPAN元素组成,这里用到了文本反爬机制,利用CSS控制文本偏移来实现文本顺序改变。但不难发现其文本偏移由left属性决定原文正确顺序,因此需要按偏移left属性值大小升序排序获取正确的文本顺序。
    1. # 获取书籍名字
    2. for tag in soup.select("h3.name"):
    3. if "whole" in tag.attrs['class']:
    4. names.append(tag.text)
    5. else:
    6. chars = tag.select("span.char")
    7. chars = sorted(chars, key=lambda a: eval(a.attrs['style'][6:-3]))
    8. name = ""
    9. for char in chars:
    10. name += char.text.strip()
    11. names.append(name)
    • 作者可以直接p.class查询
    1. # 获取作者名字
    2. for tag in soup.select("p.authors"):
    3. authors.append(tag.text.strip().replace(" ", "").replace("\n", ""))

    2. 改进要求A

            这里实现的是选项一:实现可以遍历网站的每一页来爬取书籍信息。从游览器url: https://antispider3.scrape.center/page/2 得之每页都是在后边加/page/页数,这不难实现,就是写个文本数字追加到url后即可;

    1. url = "https://antispider3.scrape.center/page/"
    2. page_start = int(input("请指定爬取起始页(包含该页):"))
    3. page_end = int(input("请指定爬取结束页(不包含该页):"))
    4. for i in range(page_start, page_end):
    5. names, pics, authors, links = get_cover(url + str(i))

             指定爬取条目数量,当爬取总条目满足数量后停止爬取,这个就是在循环爬取写个计数器,爬取到指定数目,break即可,但只得注意的是:指定数量超过一页18条时,继续下一页爬取,也可以直接加在上述代码里,把结束页可以给的很大,用计数器break即可,不会造成伪死循环。

    3. 改进要求B:

            从上图页面分析得知:每本书得二级页面都是在https://antispider3.scrape.center后加/detail/数字,该部分网址在a标签得href属性里,由于页面里超链接很多,所以先find_all出div下的class=el-col el-col-24,这里用得class_是为了解决class是python中的关键字问题,爬取后与原始url拼接即可。

    1. # 获取每本书对用url(二级页面)
    2. tags = soup.find_all('div', class_='el-col el-col-24')
    3. print(len(tags))
    4. for tag in [tags[i] for i in range(len(tags)) if i % 2 == 0]:
    5. link = tag.find('a').get('href')
    6. links.append(url1 + link)
    7. print(links)

            现在得到了每本书得二级页面得url,就可以分析二级页面页面结构,来爬取相应书籍信息,分析如下所示:

             二级页面结构其实还是清新明了的,出了评分时span标签,再其他都是p标签,这里只爬取了上图标注的信息数据,再爬取其他的都是一样的,换汤不换药,其实就换个class就OK,这里不做过多介绍。

             由于爬取页面过多,发现问题:有些书籍没有出版社,页数等,所以这里统一用None,没有的数据就用统一添加该字段去空即可,如做特殊处理,识别没有的信息,每个属性都要增加相同的代码,代码冗余度太高,学术水平限制,这里没想到其他好的方法,所以没有做特殊处理。爬取下来的数据由于中间有很多空格与\n,如下所示

            这里就用到77行一系列的替换,使达到想要的格式,其他类似。

            下面介绍主函数部分:

            这里将每本书的二级页面的url赋给对应属性

    1. for link in links:
    2. print(link)
    3. score, price, publishtime, publisher, page, isbm = get_details(link)

            这里遍历出每本书的信息保存在以书名为名称的json文件中。

    1. for i in range(len(names)):
    2. book = {"title": names[i],
    3. "cover_url": pics[i],
    4. "authors": authors[i],
    5. "link": links[i],
    6. "score": scores[i],
    7. "price": prices[i],
    8. "publish_time": publishtimes[i],
    9. "publishers": publishers[i],
    10. "pages": pages[i],
    11. "ISBM": isbms[i]
    12. }
    13. data_path = f'{book["title"]}.json'
    14. json.dump(book, open(data_path, 'w', encoding='utf-8'), ensure_ascii=False, indent=2)

            最后附上爬取结果:

     本次实现总结:

            计算机专业的课程只理论不实践那就例如纸上谈兵,本次实践说简单也不难,但有些点还是触及我的知识盲区了,例如span char的书名,实践是检验真理的唯一标准。爬虫技术有限,每次爬二级页面都要加载打开,很浪费时间的,后期学了更多的知识,再来解决此问题吧。


    六、资料

    1.实验框架代码:

    1. from selenium import webdriver
    2. from pyquery import PyQuery as pq
    3. from selenium.webdriver.common.by import By
    4. from selenium.webdriver.support import expected_conditions as EC
    5. from selenium.webdriver.support.wait import WebDriverWait
    6. browser = webdriver.Chrome()
    7. browser.get('https://antispider3.scrape.center/')
    8. WebDriverWait(browser, 10) \
    9.     .until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.item')))
    10. html = browser.page_source
    11. doc = pq(html)
    12. names = doc('.item .name')
    13. for name in names.items():
    14. print(name.text())

    2.OpenSSL:Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions (slproweb.com)

    3.Josn存储,先安装json包:

    1. import json
    2. book = {"title": "Wonder",
    3.     "cover_url":"https://img1.doubanio.com/view/subject/l/public/s27252687.jpg",
    4.     "authors":"R. J. Palacio"
    5.     }
    6. data_path = f'{book["title"]}.json'
    7. json.dump(book, open(data_path, 'w', encoding='utf-8'), ensure_ascii=False, indent=2)

    4.实验小提示

            可以根据HTML结构发现每个书籍信息都保存在。有的书名放在class="name whole"的H3元素,有书名由多个class="char"的SPAN元素组成。对于放在H3元素的书名,直接取出其元素内容即可,而对于放在多个SPAN元素中的书名,这里用到了文本反爬机制,利用CSS控制文本偏移来实现文本顺序改变。但不难发现其文本偏移由left属性决定原文正确顺序,因此需要按偏移left属性值大小升序排序获取正确的文本顺序。


    七、源码

    1. import json
    2. import warnings
    3. from selenium import webdriver
    4. from pyquery import PyQuery as pq
    5. from selenium.webdriver.common.by import By
    6. from selenium.webdriver.support import expected_conditions as EC
    7. from selenium.webdriver.support.wait import WebDriverWait
    8. from bs4 import BeautifulSoup
    9. # 定义容器用来存储书籍的信息
    10. names = [] # 书籍名字
    11. authors = [] # 书籍作者
    12. pics = [] # 书籍封面图片
    13. links = [] # 链接
    14. scores = [] # 评分
    15. prices = [] # 定价
    16. publishtimes = [] # 出版时间
    17. publishers = [] # 出版社
    18. pages = [] # 页数
    19. isbms = [] # ISBM
    20. # 获取书籍分面信息与对应书籍二级页面url
    21. def get_cover(url):
    22. warnings.filterwarnings('ignore')
    23. browser = webdriver.Chrome()
    24. browser.get(url)
    25. WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.item')))
    26. html = browser.page_source
    27. doc = pq(html)
    28. # 使用BeautifulSoup进行解析网页
    29. soup = BeautifulSoup(doc.html(), "html.parser")
    30. browser.close()
    31. # 获取书籍名字
    32. for tag in soup.select("h3.name"):
    33. if "whole" in tag.attrs['class']:
    34. names.append(tag.text)
    35. else:
    36. chars = tag.select("span.char")
    37. chars = sorted(chars, key=lambda a: eval(a.attrs['style'][6:-3]))
    38. name = ""
    39. for char in chars:
    40. name += char.text.strip()
    41. names.append(name)
    42. # 获取作者名字
    43. for tag in soup.select("p.authors"):
    44. authors.append(tag.text.strip().replace(" ", "").replace("\n", ""))
    45. # 获取书籍封面图片url
    46. for tag in soup.select("img.cover"):
    47. pics.append(tag.attrs['src'])
    48. # 获取每本书对用url(二级页面)
    49. tags = soup.find_all('div', class_='el-col el-col-24')
    50. print(len(tags))
    51. for tag in [tags[i] for i in range(len(tags)) if i % 2 == 0]:
    52. link = tag.find('a').get('href')
    53. links.append(url1 + link)
    54. print(links)
    55. return names, pics, authors, links
    56. # 获取每本书的详细信息(二级页面信息)
    57. def get_details(url):
    58. warnings.filterwarnings('ignore')
    59. browser = webdriver.Chrome()
    60. browser.get(url)
    61. WebDriverWait(browser, 300).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.item')))
    62. html = browser.page_source
    63. doc = pq(html)
    64. # 使用BeautifulSoup进行解析网页
    65. soup = BeautifulSoup(doc.html(), "html.parser")
    66. # 获取评分
    67. score = soup.find('span', class_='score m-r m-b-sm')
    68. if score != None:
    69. score = score.text
    70. score = str(score).replace(' ', '').replace('\t', '').replace('\n', '')
    71. else:
    72. score = ' '
    73. scores.append(score)
    74. # 获取定价
    75. price = soup.find('p', class_='price')
    76. if price != None:
    77. price = price.text
    78. price = str(price).replace(' ', '').replace('\t', '').replace('\n', '').split(':')[1]
    79. else:
    80. price = ' '
    81. prices.append(price)
    82. # 获取出版时间
    83. publishtime = soup.find('p', class_='published-at')
    84. if publishtime != None:
    85. publishtime = publishtime.text
    86. publishtime = str(publishtime).replace(' ', '').replace('\t', '').replace('\n', '').split(':')[1]
    87. else:
    88. publishtime = ' '
    89. publishtimes.append(publishtime)
    90. # 获取出版社
    91. publisher = soup.find('p', class_='publisher')
    92. if publisher != None:
    93. publisher = publisher.text
    94. publisher = str(publisher).replace(' ', '').replace('\t', '').replace('\n', '').split(':')[1]
    95. else:
    96. publisher = ' '
    97. publishers.append(publisher)
    98. # 获取页数
    99. page = soup.find('p', class_='page-number')
    100. if page != None:
    101. page = page.text
    102. page = str(page).replace(' ', '').replace('\t', '').replace('\n', '').split(':')[1]
    103. else:
    104. page = ' '
    105. pages.append(page)
    106. # 获取ISBM
    107. isbm = soup.find('p', class_='isbn')
    108. if isbm != None:
    109. isbm = isbm.text
    110. isbm = str(isbm).replace(' ', '').replace('\t', '').replace('\n', '').split(':')[1]
    111. else:
    112. isbm = ' '
    113. isbms.append(isbm)
    114. browser.close()
    115. return score, price, publishtime, publisher, page, isbm
    116. if __name__ == '__main__':
    117. url1 = "https://antispider3.scrape.center"
    118. url = "https://antispider3.scrape.center/page/"
    119. page_start = int(input("请指定爬取起始页(包含该页):"))
    120. page_end = int(input("请指定爬取结束页(不包含该页):"))
    121. for i in range(page_start, page_end):
    122. names, pics, authors, links = get_cover(url + str(i))
    123. for link in links:
    124. print(link)
    125. score, price, publishtime, publisher, page, isbm = get_details(link)
    126. for i in range(len(names)):
    127. book = {"title": names[i],
    128. "cover_url": pics[i],
    129. "authors": authors[i],
    130. "link": links[i],
    131. "score": scores[i],
    132. "price": prices[i],
    133. "publish_time": publishtimes[i],
    134. "publishers": publishers[i],
    135. "pages": pages[i],
    136. "ISBM": isbms[i]
    137. }
    138. data_path = f'{book["title"]}.json'
    139. json.dump(book, open(data_path, 'w', encoding='utf-8'), ensure_ascii=False, indent=2)

    下一篇文章: 实验项目二:模拟登录和数据持久化

    实战源码:Python网络爬虫实战 

  • 相关阅读:
    Git基本指令
    K8S获取连接token
    全网最详细安装 IntelliJ IDEA (原理+方法)不看别后悔
    安科瑞EMS能效管理平台降低铜电解单耗的应用-Susie 周
    最简单的git图解(最基本命令)
    stm32学习笔记:GPIO输入
    【3D建模制作技巧分享】Maya模型如何导入zbrush
    机器学习算法(八):基于BP神经网络的乳腺癌的分类预测
    多元函数的微分法
    使用 webpack 打包 typescript(.ts); babel.js 做低版本浏览器兼容
  • 原文地址:https://blog.csdn.net/qq_57268251/article/details/127523752