小白的爬虫学习

前言

一开始学习爬虫是在一门课程当中,当时听老师讲完觉得好像都能听懂,但是到自己产生需求面对不同的网页的时候,自己写的时候发现各种问题。由于并没有学习前端的基础,所以很多都是边问大佬边search,反正以解决当下问题为目标。
想到要来写博客也是因为近期被同学问到爬虫,发现上次需求解决的时候感觉自己已经对啥啥啥都比较了解了而现在发现全忘了。真是捡了忘、忘了捡、捡了用再次忘的无限循环。所以也许写个博客记录一下比较好,顺便督促自己学习,哈哈哈。

普通爬虫

爬虫主要有两步,第一步是发送网络请求,也就是把网络源码扒下来;第二步是数据清洗,即找到源码中自己想要的内容。对于普通网页,第一步比较简单,各个语言中都有很多工具包直接用就行了,比如python中的requests、urllib2等等。而我后面两节要说的就是存在幺蛾子的情况。第二步的数据清洗则一般有两种方法,一种是通过正则表达式去匹配找到目标内容,这是我最开始喜欢的方法,python中主要用到re包;另一种是通过HTML机构进行查找,这个应该就是如果懂前端的话就很好理解了,但是我觉得对于只是爬虫应该差不多了解就行了,python中比如BeautifulSoup。
顺便说一下请求头的这个东西,一开始绝对好像没什么必要,但是如果是涉及到登录cookies这些东西还是很有必要写请求头的。
因为以前没什么留代码的习惯(其实是我已经忘了当时写的啥),所以这里只好放一个之前自己学习BeautifulSoup的代码笔记了,这个代码应该也是从某位前辈的博客抄过来的.

from bs4 import BeautifulSoup
html = '<p class="title" id="p1"><b>The Dormouses story</b></p >'
soup = BeautifulSoup(html, 'lxml')
#根据class的名称获取p标签内的所有内容
soup.find(class_="title")
#或者
soup.find("p",class_="title" ,id = "p1")
#获取class为title的p标签的文本内容"The Dormouse's story"
soup.find(class_="title").get_text()
#获取文本内容时可以指定不同标签之间的分隔符,也可以选择是否去掉前后的空白。
soup = BeautifulSoup('<p class="title" id="p1"><b> The Dormouses story </b></p ><p class="title" id="p1"><b>The Dormouses story</b></p >', "html5lib")
soup.find(class_="title").get_text("|", strip=True)
#结果为:The Dormouses story|The Dormouses story
#获取class为title的p标签的id
soup.find(class_="title").get("id")
#对class名称正则:
soup.find_all(class_=re.compile("tit"))
#recursive参数,recursive=False时,只find当前标签的第一级子标签的数据
soup = BeautifulSoup('<html><head><title>abc','lxml')
soup.html.find_all("title", recursive=False)

soup = BeautifulSoup('<p class="title" id="p1"><b> The like story </b></p ><p class="title" id="p1"><b>The Dormouses story</b></p >', "html5lib")
#获取所有class为title的标签
for i in soup.find_all(class_="title"):
    print(i.get_text())
#获取特定数量的class为title的标签
for i in soup.find_all(class_="title",limit = 1):
    print(i.get_text())

html = '<a alog-action="qb-ask-uname" href=" " rel="external nofollow" target="_blank">蜗牛宋</a >'
soup = BeautifulSoup(html, 'lxml')
# 获取"蜗牛宋",此时,该标签里既没有class也没有id,需要根据其属性来定义获取规则
author = soup.find('a',{"alog-action":"qb-ask-uname"}).get_text()
#或
author = soup.find(attrs={"alog-action": "qb-ask-uname"})

#找前头和后头的标签
soup.find_all_previous("p")
soup.find_previous("p")
soup.find_all_next("p")
soup.find_next("p")
#找父标签
soup.find_parents("div")
soup.find_parent("div")

存在动态加载的情况

这一次是因为当时在做一个比赛,正好需要一个碳排放的数据。然后我同学跟我说能不能把数据爬下来,于是就去尝试了一下。一开始也觉得没有什么问题,但是因为需要的数据是weighted Carbon Price,需要点击修改条件,然而点击之后网页地址并没有变,但源码是变了的,如果按普通的爬虫则爬下来的不是想要的数据。当时一脸懵逼的去各种search, 然后发现是一种叫做ajax动态加载的东西。大概就是不重新加载整个网页,只对部分内容进行更新。
这就得先了解一下HTTP的请求方法GETPOST,普通网页就一般用get就行了,直接获取服务器中的某个资源;post是用于向服务器提交数据,比如说登录啊之类。
存在动态加载情况的就需要用到post,我一开始以为是各种请求头的问题,后来发现是得找对网页加载对应的请求是什么。在浏览器F12查看network,可以发现点击条件之后网页出现了新的请求,也能看到请求方法是POST,在我的这个例子中最重要的是那个Request URL,他是变化了的,并不是浏览器中的那个链接!往下看对应的Request Payload中的内容刚好就是所选择的条件,即请求内容。
1
2

找到了问题的关键,得加入请求,并且找对对应的链接。我不知道是不是所有的动态加载情况他的网页地址都会变化,等下次遇到希望还能想得起来注意下。
下面是我当时写的代码。

import urllib
import re
import pandas as pd
import time
import json
import requests
from html.parser import HTMLParser

#from urllib.parse import urlencode
payloadData = {
        'HistoryPeriod' : 'None',
        'PriceType':'weightedCarbonPrice'
        }
        
payloadHeader = {
        'Host':'indices.ihsmarkit.com',
        'Content-Type': 'application/json',
        'Accept': '*/*',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Connection': 'keep-alive',
        'X-Requested-With': 'XMLHttpRequest',
        'Origin': 'https://indices.ihsmarkit.com',
        'Referer': 'https://indices.ihsmarkit.com/Carbonindex'        
        }

#url = 'https://indices.ihsmarkit.com/Carbonindex/'#并不是这个链接
url = 'https://indices.ihsmarkit.com/CarbonIndex/GetIndexHistoryChart'

r = requests.post(url,data=json.dumps(payloadData),headers=payloadHeader)
html = str(r.json())
html = re.sub(r'\n','',html) 
print(html)

info = re.compile(r'Data&quot;:.*?&quot;EndOfMonthPoints&quot;')
data = info.findall(html)
print(data)
dat = str(data).split("}")

value_pat = re.compile(r'Value&quot;:.*,')
date_pat = re.compile(r'Date&quot;:.*')
res=[]
for i in range(0,len(dat)-1):
    values = value_pat.findall(dat[i])
    value = re.sub('Value&quot;:','',values[0])
    value = re.sub(',','',value)
    dates = date_pat.findall(dat[i])
    date = re.sub('Date&quot;:','',dates[0])
    times = float(int(date)/1000)
    standardtime = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(times))    
    res.append([value,standardtime])
    
res_csv = pd.DataFrame(res,columns=['value','date'])
res_csv.to_csv('data1.csv', index=False, encoding='gbk')

请求头应该不用写那么长,我当时就是各种尝试,多了比少了好。。。而且我也怀疑*data=json.dumps(payloadData)这个东西到底是不是必须的,虽然数据是json格式这是肯定的。应该能看出来以前喜欢用正则表达式,哈哈哈哈。

JS网页渲染

这一次是我同学让我帮他怕企业信息的对外投资信息
举例图片
这个网页看着普通,但是你查看网页源码,发现一堆里面就没有几个汉字的,根本没有想要的信息。但是F12的elements里面啥都有。一开始特别懵逼,后来去问了一个前端的同学说“因为检查元素是经过页面渲染之后的,而源代码是要执行某些函数,操作然后再渲染页面”。看了一下确实是有js渲染。这个时候就又开始search,后来发现了大家都用的工具selenium。这个包应该是很独立的,我本着解决问题就行的思路于是只学了小部分,后面再有需求继续学习,中文教程
传送门
。安装略麻烦,参考博客

from selenium import webdriver
import time
 
browser = webdriver.Chrome()
browser.get("https://aiqicha.baidu.com/company_detail_18394935087267?rq=ef&pd=ee&from=ps")
#看一下要点多少次
num = browser.find_elements_by_class_name("ivu-page-item")
#n = len(num) #这个网址是2
contents = []
for i in range(len(num)):
    if len(contents)==0:
        content = browser.find_elements_by_class_name("ellipsis-line-2")
        contents = contents + [content[i].text for i in range(len(content))]
    else:
        button = browser.find_element_by_class_name("ivu-page-next")
        button.click()
        time.sleep(1)
        content = browser.find_elements_by_class_name("ellipsis-line-2")
        contents = contents + [content[i].text for i in range(len(content))]

browser.close()

一开始觉得好像还挺麻烦,了解了这个库之后觉得简直就是神器。此外一直想着用正则匹配的我也觉得果然还是按结构查找比较方便。

小白一枚,几乎都是本着能解决我的问题就行的原则去搜搜捡捡,如果有什么不对的地方还望各位大侠多多指点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值