2021SC@SDUSC
ECommerceCrawlers/TouTiao详解
文章目录
一、代码概况
爬虫功能
在头条中搜索某个指定字段,并将搜索结果中所有的文章用csv格式存储。
代码位置
在项目中的位置:ECommerceCrawlers/TouTiao
gitee中的位置:https://gitee.com/AJay13/ECommerceCrawlers/tree/master/TouTiao
文件夹结构
├─TouTiao
│ ├─pictures
│ │ ├─JosephNest.jpg
│ │ └─mortaltiger.jpg
│ ├─README.md
│ └─toutiao.py
- Toutiao:TouTiao爬虫的最上级目录名
- pictures:图片目录,用于README.md引用,该目录下的两张图片是微信二维码
- README.md:头条爬虫的说明文档
- toutiao.py:爬虫代码(重点)
二、代码详解
导库
import requests
import time
from selenium import webdriver
import csv
import pandas as pd
from urllib.parse import quote
from fake_useragent import UserAgent
requests
库:是一个Python第三方库,是非常好用的http网络请求库
time
库:Python自带的时间库
selenium
库:用于Web自动化测试(学爬虫非常不推荐的库!)
csv
库:顾名思义,用来操作csv的
pandas
库:基于Numpy的数据分析库,非常强大,爬虫中常用于存取数据和保存数据
from urllib.parse import quote
:quote方法用于拼接url
from fake_useragent import UserAgent
:fake_useragent库用来伪装UA
评价与改进
从导库混乱就可以看出来作者在写这个爬虫的时候是可能是刚刚入门爬虫,为什么这么说,首先,他在调用了selenium
库,这说明他在某一步骤的网络请求无法通过requests
库发送http请求获得正确的返回结果。其次,他同时调用了pandas
和csv
库,很显然,他想要将爬取的数据用csv存储,但他显然并不能熟练地使用这两个库。事实上,要想将数据写入csv,我们可以用pandas.DataFrame()获取一个DataFrame对象,再使用DataFrame对象的to_csv()
方法即可。最后,from urllib.parse import quote
完全可以用功能更全面的from urllib.parse import urlencode
替代。
爬虫本体(逐行分析,看注释)
# base_url 存储的是要爬取网站的基础网址,一般用于拼接
base_url = 'https://www.toutiao.com/api/search/content/'
# 时间戳,一般用于与url拼接,是url中的一个参数,
# 乘1000的原因是:JS中的Date.now()是13位,而Python中的time.time()是10位
timestamp = int(time.time()*1000)
# ua是fake_useragent.UserAgent()的实例化对象,一般用于伪造headers中的UA,
# 常见写法是fake_useragent.UserAgent().random
# verify_ssl=False表示忽略 SSL 验证
ua = UserAgent(verify_ssl=False)
# 初始化空列表article_url_list,用于存储文章链接
article_url_list = []
# 没看懂要干啥
csv_name = pd.read_csv("typhoon_toutiao.csv")
# 下边10行代码是用来使用代理ip的,这类代码的具体写法得去看每个代理ip网站的使用文档,当你要用到代理ip的时候自然会知道的。
page_urls = ["http://dev.kdlapi.com/testproxy",
"https://dev.kdlapi.com/testproxy",]
tunnel_host = "tps189.kdlapi.com"
tunnel_port = "15818"
tid = "xxx"
password = "xxx"
proxies = {
"http": "http://%s:%s@%s:%s/" % (tid, password, tunnel_host, tunnel_port),
"https": "https://%s:%s@%s:%s/" % (tid, password, tunnel_host, tunnel_port)
}
# 初始化constract_list用于去重
constract_list = []
# 这个方法用于获取搜索结果是name的所有文章的url
def get_article_urls(name):
# 拼接网址,我们能很明显的看出,下面的网址是用来搜索的,搜索的内容是name的值
decde = quote(name)
referer = 'https://www.toutiao.com/search/?keyword='+decde
# 着重说一下下面的offset,offset的中文意思是偏移量。
# 在爬虫中,offset一般用于翻页,
# 比如说在某个网页中,一页有20条链接,我们在第一页时,offset=0 (0*20),
# 当我们要请求第二页的数据时,需要将offset置为20 (1*20),以此类推。
# offset一般和limit一起使用
# 下面的offset的值在循环中依次为0、20、40、60、80、100
for offset in range(0, 120, 20):
# params是GET请求的参数,用于给服务器发送数据的,也可以直接写在URL中,
# 写在params中,用requests.get(url=url,params=params,headers=headers)的写法会更美观优雅一些。
params = {
'aid': 24,
'app_name': 'web_search',
'offset': offset,
'format': 'json',
'keyword': name,
'autoload': 'true',
'count': 20,
'en_qc': 1,
'cur_tab': 1,
'from': 'search_tab',
'pd': 'synthesis',
'timestamp': timestamp
}
# headers 请求头,抓包获取即可,
# ua.random用于生成随机UA,是最基本的反爬方法,这个在上边已经说过了,就不细说了。
headers = {
'cookie': 'tt_webid=6781305717874820616; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6781305717874820616; s_v_web_id=59cfa658a89df645e8a82f1618a81bd0; __tasessionId=g8ptymp5v1579144106433',
'user-agent': ua.random,
'x-requested-with': 'XMLHttpRequest',
'referer': referer,
}
# 用get请求获取返回结果,get中的proxies也是代理ip的参数,当你真正需要用到代理ip的时候自然会知道怎么用。
html = requests.get(url=base_url, params=params,
headers=headers, proxies=proxies)
# 将返回结果转化为json格式,并获取data参数,将所有的data合成为一个列表
result = list(html.json().get('data'))
# 下面这个循环用于解析上面的data列表,
# 此处要注意,result是一个data的列表,data是json格式的,不能使用字典的方法取值,而是要使用get()方法
for item in result:
# 提取每篇文章的url
article_url = item.get('article_url')
# 下面这行我测试了一下,今日头条返回的数据里的网址不一定能打开,
# 一定是要包含'http://toutiao.com/'的打开才有内容,
# 至于mp4和长度<100,没看出来是干啥用的。
if article_url and len(article_url) < 100 and (".mp4" not in article_url) and "toutiao.com" in article_url:
# 至于为什么要将/group/换成/a,是因为今日头条的重定向,具体的可以看下边的例子
# 这是一条可以访问的url:http://toutiao.com/group/7016575740439249441/,
# 当你将它在浏览器中打开时,他会重定向为 https://www.toutiao.com/a7016575740439249441/
if '/group/' in article_url:
article_url = article_url.replace(
'/group/', '/a').replace('http://', 'https://www.')
# 将获取到的url添加到article_url_list
article_url_list.append(article_url)
print(article_url)
# 这个方法用于控制发送请求和存储
def request_AND_storage(name):
# 文件名赋值为 name+".csv" ,应该是要将搜索结果存到相对应名称的csv中
filename = name+".csv"
# 异常处理,网站的反爬机制或者异常响应都可能导致函数的异常,
# 为了保证函数能够运行下去,一般会使用异常处理
try:
# 调用上面的方法获取url列表
get_article_urls(name)
except Exception as e:
print(e)
# 为什么要用selenium?我实在不理解
browser = webdriver.Chrome()
# 程序休眠2s
time.sleep(2)
# 下面开始一条条获取article_url_list中的每篇文章的具体内容
for url in article_url_list:
print(url)
try:
# 在浏览器中模拟访问文章的url
browser.get(url)
# 程序休眠1s
time.sleep(1)
# 今日头条改版面了,下面这个xpath已经不对了,
# 目前要想获取整篇文章需要先搜索xpath://article,
# 再将//article下的所有<p>节点的文字内容进行拼接,所有<img>中的href提取。
text_res = browser.find_element_by_xpath(
'//div[@class="article-box"]')
# 下面的代码已经没用了
print(text_res)
text_res = text_res.text
print(text_res)
# 上面的代码已经没用了
# 将文章内容存储到csv中,我还是不理解为什么要同时调pandas和csv两个库...
with open(filename, 'a', encoding='utf-8') as f:
writer = csv.writer(f)
L = [name, text_res]
writer.writerow(L)
except:
continue
# 退出selenium的webdriver
browser.close()
# 函数入口
if __name__ == '__main__':
# 异常处理
try:
# 尝试搜索“武汉疫情”并保存
request_AND_storage('武汉疫情')
# 如果下边还有进行搜索的话,需要重置article_url_list,只搜索一条的话这句话就显得多此一举了,
# 而且这句话应该写在finally里边,而不是在try和except里各写一遍。
article_url_list = []
#休眠10s
time.sleep(10)
except Exception as e:
print(e)
article_url_list = []
time.sleep(1)
评价与改进
从整个逻辑上来看,作者在写这个今日头条的爬虫的时候逻辑还是比较清晰的,但是从库和函数的使用上来说,这位作者显然是不太熟练,以至于出现了调用不同的库去实现同一个功能的做法。
在此我还是要强调一下,学爬虫别用selenium!学爬虫别用selenium!学爬虫别用selenium!selenium只能应急使用,能不用尽量别用,对初学者理解HTTP协议没有任何帮助!
明天要出去恰饭,下周我会把修改好的今日头条的爬虫发出来。