2021SC@SDUSC
在头条中搜索某个指定字段,并将搜索结果中所有的文章用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:爬虫代码(重点)
# 这个方法用于获取搜索结果是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,对理解HTTP协议几乎没有任何帮助。