《Python网络爬虫从入门到实践》自学笔记+疑难解决

写在前面:刚刚开始学习python,想直接从爬虫入手,下面的内容是摘自书本的笔记以及一些个人感悟和遇到的一些问题。有不对的地方大家一定提出来,谢谢
附上我看的书的PDF:qpu2

第一章——网络爬虫入门

笔记:

  • 概述:简单来说,平时在网上浏览网站时所能见到的数据都可以通过爬虫程序保存下
  • 爬虫流程:(1)获取网页(2)解析网页/提取数据(3)存储数据

第二章——编写第一个网络爬虫

试题5:排序函数sorted,item()将字典的键值对转换成元组,然后再组合成一个list的意思,key值是使用operator.itemgetter函数,按照指定的[1]排序,这里[0]是key,[1]是value

 # __author: HY
 # date: 2019/3/24
 
  # !/uer/bin/python
  # coding: UTF-8
  
  import requests
  from bs4 import BeautifulSoup
  link = "http://www.santostang.com/"
 headers = {'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
 
 r = requests.get(link, headers=headers)
 
 soup = BeautifulSoup(r.text, "lxml")
 title = soup.find("h1", class_="post-title").a.text.strip()
 print(r.text)
 
 with open('title.txt', "a+") as f:
     f.write(title)
     f.close()

第三章——静态网页抓取

笔记:

  • Requests

获取豆瓣电影top25的时候,列表那里的class报错语法有误,暂时不知道什么原因,发现上面的代码是有的,把class改成class_就没问题了,可能是作者漏了?

  # __author: HY
  # date: 2019/3/24
 
  import requests
  from bs4 import BeautifulSoup
  
  
  def get_movies():
      #   User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko我用的是微软的浏览器,源代码是这样的,但按照书上的打也没问题。
       
            headers = \
         {'User-Agent': 'Mozilla/5.0 (Windows NT6.1; Win64; x64)AppleWebKit/537.36 '
                        '(KHTML, like Gecko)chrome/52.0.2743.82Safari/537.36', 'Host': 'movie.douban.com'}
     movie_list = []
     for i in range(0, 10):
         link = 'https://movie.douban.com/top250?start=' + str(i * 25)
         r = requests.get(link, headers=headers, timeout=10)
         print(str(i+1), '页响应状态码:', r.status_code)
         soup = BeautifulSoup(r.text, "lxml")
         div_list = soup.find_all('div', class_='hd') 
         # div_list = soup.find_all('<div class="hd">')
         for each in div_list:
             movie = each.a.span.text.strip()
             movie_list.append(movie)
     return movie_list
 
 
 movies = get_movies()
 print(movies)

第四章——动态网页抓取

笔记:

  • 爬取动态加载的内容:
    • 浏览器审查元素解析地址
    • Selenium模拟浏览器抓取

47页的代码,我没有用火狐浏览器,而是用了微软的Edge,报错如下:

selenium.common.exceptions.WebDriverException: Message: ‘MicrosoftWebDriver.exe’ executable needs to be in PATH.Please download from http://go.microsoft.com/fwlink/?LinkId=619687

反正就是缺个驱动的问题,点击报错内容那里的链接下载了适合的驱动
百度一下,参考网友做法,不需要配置环境变量,直接就成功了,借鉴了这篇文章


  # __author: HY
  # date: 2019/3/25
  
  from selenium import webdriver
  
  driver = webdriver.Edge(r'E:\我的安装\微软驱动\MicrosoftWebDriver.exe')
  driver.get("http://www.baidu.com")
  print(driver.title)
 # time.sleep(5)
 # driver.quit()
 # driver.get("http://www.dianping.com/search/7/10/pl")
  • 好奇怪,发现作者给的的博客的网站都404了,不知道是什么问题
  • 51页处注意elementh和elements的区别,加了s会得到一个刘表,可以通过遍历的方法获得其中的每个元素再进行输出。
  • 适当的时候要进行sleep一下,因为网络没加载页面这么快,不要贪图方便省略了,有时会因此出错

最后4.4的实验尝试了一下,发现爬到的内容有些乱,没有深究,现在先打基础吧,慢慢来

第五章——解析网页

笔记

HTML解析器性能易用性提取数据的方式
正则表达式较难正则表达式
BeautifulSoup快(使用lxml解析)简单Find方法
CSS选择器
lxml较难XPath
CSS选择器
  • 常见的正则字符和含义:
模式描述
.匹配任意字符,除了换行符
*匹配前一个字符的0次或多次
+匹配前一个字符1次或多次
?匹配前一个字符0次或1次
^匹配字符串开头
$匹配字符串末尾
()匹配括号内的表达式,也表示一个组
\s匹配空白字符
\S匹配任何非空白字符
\d匹配任何数字,等价于[0-9]
\D匹配任何非数字,等价于[^0-9]
\w匹配字母数字,等价于[A-Za-z0-9_]
\W匹配非字母数字,等价于[^A-Za-z0-9_]
[]用来表示一串字符
  1. 正则表达式
  • 三个方法:
    (1)re.match():必须从开头匹配,只能找一个
    (2)re.search():全文匹配,但也只能找一个
    (3)re.findall():最优选择,返回的是一个列表
  • 贪婪模式:.* ——尝试匹配尽可能多的字符
  • 非贪婪模式:.*?——尽量匹配尽可能少的字符
  • r:代表纯粹字符串,不会对里面的\转译
  1. BeautifulSoup
  • find_all():得到一个列表
  • find()
# __author: HY
# date: 2019/3/30

import requests
from bs4 import BeautifulSoup

# 网址
link = "http://www.santostang.com/"
# 消息头
headers = {'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
# 获取内容
r = requests.get(link, headers=headers)

# 把获取到的内容变成 BeautifulSoup对象
soup = BeautifulSoup(r.text, "html.parser")
# 找到class是post-title下a标签的文本,去空格
first_title = soup.find("h1", class_="post-title").a.text.strip()
# 输出
print(first_title)

# 找到全部class是post-title的标签,放在一个列表里,遍历输出其中的a标签的文本,去空格
title_list = soup.find_all("h1", class_="post-title")
for i in range(len(title_list)):
    title = title_list[i].a.text.strip()
    print('第 %s 篇文章的标题是: %s'%(i+1, title))

BeautifulSoup对象是一个复杂的树形结构,每一个节点都是一个python对象,获取网页内容就是一个提取对象的过程。提取对象的方法有三种:
(1)遍历文档树:

获取标签内容:
soup.header.h3#这里的header是有个标签是header
获取子节点列表:
soup.header.div.contents#也可以加上[]代表特定的哪个子节点
获取子节点列表(不能自动输出):
for child in soup.header.div.children: print(child)# 仅获得下一级节点 for child in soup.header.div.descendants: print(child)# 所有节点
获取父节点:
a_tag = soup.header.div.a a_tag.parent
(2)搜索文档树:
find()
find_all()
仅常用上面两个,可以和正则表达式结合使用

for tag in soup.find_all(re.compile("^h")):
	print(tag.name)
# 找到所有以h开头的标签。传入正则表达式作参数,BeautifulSoup通过正则的match()方法匹配。

re.compile:参数写正则就会返回匹配的列表
(3)CSS选择器
select()

  1. lxml
    这个没有详细写,所以也没有去认真看了。

PS:因为我稍微看过html、css、js的视频,他们都是用来写网页的,有各种各样的标签,对上面的知识点不存在太大问题,没学过写网页的话可能看网页源代码和python中爬取网页的知识会有些吃力。如果有小伙伴需要写网页的视频可以找我。

第六章——数据存储

  1. 存储在文件中,TXT,CSV文件
  2. 存储在数据库中,MySQL关系数据库和MongoDB数据库

将数据写入TXT文件:

# 以a+的方式打开文件test.txt,写入内容。
# 最后我记得是不需要关闭的,这个语法格式会自动关闭,但书上写了
title= “This is a test sentence."
with open (r'D:\test.txt',"a+" as f:
	f.wtrite(title)
	f.close()

# 用tab将变量分割,如果是大量需要的,在循环里推导一下也行
output= '\t'.join(['name', 'title', 'age', 'gender'])
with open (r'D:\test.txt',"a+") as f:
	f.wtrite(output)
	f.close()

with open (r'D:\test.txt',"r", encoding = 'UTF-8') as f:
	r = f.read()
	print(r)

将数据写入CSV文件:

 # 详见P83.读取数据:
import csv
with open ('test.csv',"r", encoding = 'UTF-8') as f:
	r = csv.read(f)
	for row in r
		print(r)
		print(r[0])

# 写入数据:
import csv
output_list = ['1','2','3','4']
with open ('test.csv',"a+", encoding = 'UTF-8', newline=' ') as f:
	r = csv.read(f)
	r.writerow(output_list     )# 然后用excel打开即可

P103页MongoDB爬虫实践:虎扑论坛

  • 我打开查看源文件的时候,看到的数据所在位置和书上给的完全不一样,使用的标签也不一样,很奇怪,也许是虎扑定期更新了?还是说我用不一样浏览器的原因?所以只好自己查看网页源代码然后一一找出位置。我是在2019–3-31这天完成了,顺利爬到了所有内容,以后不知道还会不会改变。
  • 我的代码分成了三部分,写在三个不同的.py文件里。要先设置,然后导入才能跨文件使用。
  • 报错如下:(大致意思就是找不到那个方法/类)
    This inspection detects names that should resolve but don’t. Due to dynamic dispatch and duck typing, this is possible in a limited but useful number of cases. Top-level and class-level items are supported better than instance items.
    参考:
    作者:顺顺顺子
    原文:https://blog.csdn.net/xiaoshunzi111/article/details/78500100
    我右击了整个文件夹,才找到Mark Directory as。
  • 然后from DBclass import MongoAPI,从 DBclass 文件中导入 MongoAPI类,from get_hupu import get_data从get_hupu文件导入get_data方法
数据位置
某帖子所有数据‘ul’, class_=‘for-list’
其下的所有‘li’
帖子名称‘a’, class_=“truetit”
帖子链接‘a’ ,class_=“truetit”>[‘href’]
作者‘div’, class_=‘author’>a
作者链接‘div’, class_=‘author’>a[‘href’]
创建时间-
回复数‘span’, class_=‘ansour’
浏览数‘span’, class_=‘ansour’
最后回复用户‘span’, class_=‘endauthor’
最后恢复时间‘div’, class_=‘endreply’>a
# __author: HY
# date: 2019/3/31

# P104,本段代码如果去掉最后的导入数据库的代码,可以独立运行,输出第一页咨询

from DBclass import MongoAPI
import requests
from bs4 import BeautifulSoup
import datetime
# import time
# import re
# 测试的时候想着会不会是网速问题,sleep一下,还有contents用不了换成正则试试?所以导入了上面两个包
# 最后都没有用上,但还是保留下来吧,作为一个提醒


def get_page(link):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
    r = requests.get(link, headers=headers)
    html = r.content
    html = html.decode('UTF-8')
    soup = BeautifulSoup(html, 'lxml')
    return soup


def get_data(post_list):
    data_list = []
    for post in post_list:
        # 查看网页源代码的时候,是class='titlelink box',是两个类名titlelink和box,写一个就行了
        title_td = post.find('div', class_='titlelink')
        # 获取内容并去空格
        title = title_td.find('a',  class_="truetit").text.strip()
        # 获取其中的链接,href是链接
        post_link = title_td.find('a', class_="truetit")['href']
        # 观察,拼接,得到每个子连接
        post_link = 'https://bbs.hupu.com'+post_link

        author = post.find('div', class_='author').a.text.strip()
        author_page = post.find('div', class_='author').a['href']

        # 按照课本格式,时间获取为\n,报错
        # start_data = post.find('div', class_='author').contents[2]# 注意那个<br/>也是一个元素,所以索引是2
        # 做了以下测试,发现确实是没有获得到时间的内容contents有问题,连同下面那个也是一样的问题
        # if start_data == '\n':
        #     start_data = '2019-03-31'
        # 一开始尝试把contents换成children,但是也报错了,children[2]不是object is not subscriptable可下标访问,
        # 于是用下列遍历的方法:
        # i = 1
        # for data in start_datas:
        #     if i == 2:
        #         start_data = data.text
        #     else:
        #         i = i+1
        # 尝试通过

        # 又琢磨了一下,发现下面这样更简单,找到所有a标签,取第二个的文本:
        start_data = post.find('div', class_='author').find_all('a')[1].text

        start_data = datetime.datetime.strptime(start_data, '%Y-%m-%d').date()

        reply_view = post.find('span', class_='ansour').text.strip()
        reply = reply_view.split('/')[0].strip()
        view = reply_view.split('/')[1].strip()

        reply_time = post.find('div', class_='endreply').a.text.strip()
        # 这里用contents也有问题,于是用直接span标签,上面的没有直接取a标签是因为那个a标签没有自己的名字
        last_reply = post.find('span', class_='endauthor').text
        if ':' in reply_time:
            date_time = str(datetime.date.today()) + ' ' + reply_time
            date_time = datetime.datetime.strptime(date_time, '%Y-%m-%d %H:%M')
        else:
            date_time = datetime.datetime.strptime('2017-' + reply_time, '%Y-%m-%d').date()
        data_list.append([title, post_link, author, author_page, start_data, reply, view, last_reply, date_time])
    return data_list


link = 'https://bbs.hupu.com/bxj'
soup = get_page(link)
post_list = soup.find('ul', class_='for-list')
post_list = post_list.find_all('li')
# print(len(post_list))
date_list = get_data(post_list)
# print(len(date_list))
for each in date_list:
    print(each)


# 插入数据库的代码
hupu_post = MongoAPI('localhost', 27017, "hupu", 'post')
for each in date_list:
    hupu_post.add({'title':each[0],
                   "post_link": each[1],
                   "author": each[2],
                   "author_page": each[3],
                   "start_date": str(each[4]),
                   "reply": each[5],
                   "view": each[6],
                   "last_reply": each[7],
                   "last_reply_time": str(each[8])})
# __author: HY
# date: 2019/3/31

from pymongo import MongoClient


class MongoAPI(object):
    def __init__(self, db_ip, db_port, db_name, table_name):
        self.db_ip = db_ip
        self.db_port = db_port
        self.db_name = db_name
        self.table_name = table_name
        self.conn = MongoClient(host=self.db_ip, port=self.db_port)
        self.db = self.conn[self.db_name]
        self.table = self.db[self.table_name]

    # 获取数据库中的一条资料
    def get_one(self, query):
        return self.table.find_one(query, projection={'_id': False})

    # 获取数据库中满足条件的所有数据
    def get_all(self, query):
        return self.table.find(query)

    # 向集合中添加数据
    def add(self, kv_dict):
        return self.table.insert(kv_dict)

    # 删除集合中的数据
    def delete(self, query):
        return self.table.delete_many(query)

    # 查看集合中是否包含满足条件的数据
    def check_exist(self, query):
        ret = self.table.find_one(query)
        return ret != None

    # 更新集合中的数据,如果没有会新建
    def update(self, query, kv_dict):
        self.table.update_one(query, {
            '$set': kv_dict
        }, upsert=True)
# __author: HY
# date: 2019/3/31


from DBclass import MongoAPI
from get_hupu import get_page
from get_hupu import get_data
import requests
from bs4 import BeautifulSoup
import datetime
import time

hupu_post = MongoAPI('localhost', 27017, "hupu", 'post')
for i in range(1, 6):
    link = "https://bbs.hupu.com/bxj-" + str(i)
    soup = get_page(link)
    post_list = soup.find('ul', class_='for-list')
    post_list = post_list.find_all('li')
    date_list = get_data(post_list)
    for each in date_list:
        hupu_post.update({"post_link": each[1]}, {'title': each[0],
                       "post_link": each[1],
                       "author": each[2],
                       "author_page": each[3],
                       "start_date": str(each[4]),
                       "reply": each[5],
                       "view": each[6],
                       "last_reply": each[7],
                       "last_reply_time": str(each[8])})

    time.sleep(3)
    print('第', i, "页获取完成,休息三秒")

最后展示一下成果图:
这是输出在控制台的显示

这是在数据库的显示

最后补充一句,MongoDB数据库的安装是傻瓜式,比较简单,快完成的时候有个弹窗说要什么privilege,直接把360关闭重新安装,一切顺利~(很多时候都是360或者是防火墙的问题)

  • 11
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值