Skip to content

Commit

Permalink
Merge pull request dataabc#149 from xyauhideto/query
Browse files Browse the repository at this point in the history
feat: add keyword searching
  • Loading branch information
dataabc authored Jan 5, 2021
2 parents 3ff0d28 + cc4197a commit f4aa77b
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 18 deletions.
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"user_id_list": ["1669879400"],
"filter": 1,
"since_date": "1900-01-01",
"query_list": [],
"write_mode": ["csv"],
"original_pic_download": 1,
"retweet_pic_download": 0,
Expand All @@ -90,7 +91,7 @@
```

对于上述参数的含义以及取值范围,这里仅作简单介绍,详细信息见[程序设置](#3程序设置)
>**user_id_list**代表我们要爬取的微博用户的user_id,可以是一个或多个,也可以是文件路径,微博用户Dear-迪丽热巴的user_id为1669879400,具体如何获取user_id见[如何获取user_id](#如何获取user_id);<br>**filter**的值为1代表爬取全部原创微博,值为0代表爬取全部微博(原创+转发);<br>**since_date**代表我们要爬取since_date日期之后发布的微博,因为我要爬迪丽热巴的全部原创微博,所以since_date设置了一个非常早的值;<br>**write_mode**代表结果文件的保存类型,我想要把结果写入csv文件和json文件,所以它的值为["csv", "json"],如果你想写入数据库,具体设置见[设置数据库](#4设置数据库可选);<br>**original_pic_download**值为1代表下载原创微博中的图片,值为0代表不下载;<br>**retweet_pic_download**值为1代表下载转发微博中的图片,值为0代表不下载;<br>**original_video_download**值为1代表下载原创微博中的视频,值为0代表不下载;<br>**retweet_video_download**值为1代表下载转发微博中的视频,值为0代表不下载;<br>**cookie**是可选参数,可填可不填,具体区别见[添加cookie与不添加cookie的区别](#添加cookie与不添加cookie的区别可选)。
>**user_id_list**代表我们要爬取的微博用户的user_id,可以是一个或多个,也可以是文件路径,微博用户Dear-迪丽热巴的user_id为1669879400,具体如何获取user_id见[如何获取user_id](#如何获取user_id);<br>**filter**的值为1代表爬取全部原创微博,值为0代表爬取全部微博(原创+转发);<br>**since_date**代表我们要爬取since_date日期之后发布的微博,因为我要爬迪丽热巴的全部原创微博,所以since_date设置了一个非常早的值;<br>query_list代表要爬取的微博关键词,为空([])则爬取全部;<br>**write_mode**代表结果文件的保存类型,我想要把结果写入csv文件和json文件,所以它的值为["csv", "json"],如果你想写入数据库,具体设置见[设置数据库](#4设置数据库可选);<br>**original_pic_download**值为1代表下载原创微博中的图片,值为0代表不下载;<br>**retweet_pic_download**值为1代表下载转发微博中的图片,值为0代表不下载;<br>**original_video_download**值为1代表下载原创微博中的视频,值为0代表不下载;<br>**retweet_video_download**值为1代表下载转发微博中的视频,值为0代表不下载;<br>**cookie**是可选参数,可填可不填,具体区别见[添加cookie与不添加cookie的区别](#添加cookie与不添加cookie的区别可选)。

配置完成后运行程序:
```bash
Expand Down Expand Up @@ -265,6 +266,14 @@ since_date值可以是日期,也可以是整数。如果是日期,代表爬
```
代表爬取最近10天的微博,这个说法不是特别准确,准确说是爬取发布时间从**10天前到本程序开始执行时**之间的微博。<br>
**since_date是所有user的爬取起始时间,非常不灵活。如果你要爬多个用户,并且想单独为每个用户设置一个since_date,可以使用[定期自动爬取微博](#7定期自动爬取微博可选)方法二中的方法,该方法可以为多个用户设置不同的since_date,非常灵活**。<br>
**设置query_list(可选)**<br>
query_list是一个关键词字符串列表或以`,`分隔关键词的字符串,用于指定关键词搜索爬取,若为空`[]``""`则爬取全部微博。例如要爬取用户包含“梦想”和“希望”的微博,则设定如下:
```
"query_list": ["梦想","希望"],
"query_list": "梦想,希望",
```
请注意,关键词搜索必须设定`cookie`信息。
**query_list是所有user的爬取关键词,非常不灵活。如果你要爬多个用户,并且想单独为每个用户设置一个query_list,可以使用[定期自动爬取微博](#7定期自动爬取微博可选)方法二中的方法,该方法可以为多个用户设置不同的query_list,非常灵活**。<br>
**设置write_mode**<br>
write_mode控制结果文件格式,取值范围是csv、json、mongo和mysql,分别代表将结果文件写入csv、json、MongoDB和MySQL数据库。write_mode可以同时包含这些取值中的一个或几个,如:
```
Expand Down Expand Up @@ -323,7 +332,7 @@ $ pip install pymongo
```
MySQL和MongDB数据库的写入内容一样。程序首先会创建一个名为"weibo"的数据库,然后再创建"user"表和"weibo"表,包含爬取的所有内容。爬取到的微博**用户信息**或插入或更新,都会存储到user表里;爬取到的**微博信息**或插入或更新,都会存储到weibo表里,两个表通过user_id关联。如果想了解两个表的具体字段,请点击"详情"。
<details>

<summary>详情</summary>

**user**表<br>
Expand Down Expand Up @@ -392,7 +401,7 @@ $ python weibo.py
wb.user包含爬取到的微博用户信息,如**用户id****用户昵称****性别****生日****所在地****教育经历****公司****阳光信用****微博注册时间****微博数****粉丝数****关注数****简介****主页地址****头像url****高清头像url****微博等级****会员等级****是否认证****认证类型****认证信息**等,大家可以点击"详情"查看具体用法。

<details>

<summary>详情</summary>

**id**:微博用户id,取值方式为wb.user['id'],由一串数字组成;<br>
Expand Down Expand Up @@ -422,7 +431,7 @@ wb.user包含爬取到的微博用户信息,如**用户id**、**用户昵称**
**wb.weibo**:存储爬取到的所有微博信息;<br>
wb.weibo包含爬取到的所有微博信息,如**微博id**、**正文**、**原始图片url**、**视频url**、**位置**、**日期**、**发布工具**、**点赞数**、**转发数**、**评论数**、**话题**、**@用户**等。如果爬的是全部微博(原创+转发),除上述信息之外,还包含**原始用户id**、**原始用户昵称**、**原始微博id**、**原始微博正文**、**原始微博原始图片url**、**原始微博位置**、**原始微博日期**、**原始微博工具**、**原始微博点赞数**、**原始微博评论数**、**原始微博转发数**、**原始微博话题**、**原始微博@用户**等信息。wb.weibo是一个列表,包含了爬取的所有微博信息。wb.weibo[0]为爬取的第一条微博,wb.weibo[1]为爬取的第二条微博,以此类推。当filter=1时,wb.weibo[0]为爬取的第一条**原创**微博,以此类推。wb.weibo[0]['id']为第一条微博的id,wb.weibo[0]['text']为第一条微博的正文,wb.weibo[0]['created_at']为第一条微博的发布时间,还有其它很多信息不在赘述,大家可以点击下面的"详情"查看具体用法。
<details>

<summary>详情</summary>

**user_id**:存储微博用户id。如wb.weibo[0]['user_id']为最新一条微博的用户id;<br>
Expand All @@ -442,7 +451,7 @@ wb.weibo包含爬取到的所有微博信息,如**微博id**、**正文**、**
**at_users**:存储微博@的用户。如wb.weibo[0]['at_users']为最新一条微博@的用户,若该条微博没有@的用户,则值为'';<br>
**retweet**:存储转发微博中原始微博的全部信息。假如wb.weibo[0]为转发微博,则wb.weibo[0]['retweet']为该转发微博的原始微博,它存储的属性与wb.weibo[0]一样,只是没有retweet属性;若该条微博为原创微博,则wb[0]没有"retweet"属性,大家可以点击"详情"查看具体用法。<br>
<details>

<summary>详情</summary>

假设爬取到的第i条微博为转发微博,则它存在以下信息:<br>
Expand Down Expand Up @@ -496,14 +505,15 @@ txt文件名格式可以参考[程序设置](#3程序设置)中的设置user_id_
1223178222 胡歌 2020-01-18
1729370543 郭碧婷 2020-01-18
```
下次再爬取微博的时候,程序会把每行的时间数据作为since_date。这样的好处一是不用修改since_date,程序自动更新;二是每一个用户都可以单独拥有只属于自己的since_date,每个用户的since_date相互独立,互不干扰,格式为yyyy-mm-dd。比如,现在又添加了一个新用户,以杨紫的微博为例,你想获取她2018-01-23到现在的全部微博,可以这样修改txt文件:
下次再爬取微博的时候,程序会把每行的时间数据作为since_date。这样的好处一是不用修改since_date,程序自动更新;二是每一个用户都可以单独拥有只属于自己的since_date,每个用户的since_date相互独立,互不干扰,格式为`yyyy-mm-dd`或整数。比如,现在又添加了一个新用户,以杨紫的微博为例,你想获取她2018-01-23到现在的全部微博,可以这样修改txt文件:
```
1669879400 迪丽热巴 2020-01-18
1223178222 胡歌 2020-01-18
1729370543 郭碧婷 2020-01-18
1227368500 杨紫 2018-01-23
1227368500 杨紫 3 梦想,希望
```
注意每一行的用户配置参数以空格分隔,如果第一个参数全部由数字组成,程序就认为此行为一个用户的配置,否则程序会认为该行只是注释,跳过该行;第二个参数可以为任意格式,建议写用户昵称;第三个如果是日期格式(yyyy-mm-dd),程序就将该日期设置为用户自己的since_date,否则使用config.json中的since_date爬取该用户的微博,第二个参数和第三个参数也可以不填。
也可以设置第四个参数,将被读取为query_list。

## 如何获取user_id
1.打开网址<https://weibo.cn>,搜索我们要找的人,如"迪丽热巴",进入她的主页;<br>
Expand Down
56 changes: 45 additions & 11 deletions weibo.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ def __init__(self, config):
self.headers = {'User_Agent': user_agent, 'Cookie': cookie}
self.mysql_config = config.get('mysql_config') # MySQL数据库连接配置,可以不填
user_id_list = config['user_id_list']
query_list = config.get('query_list') or [];
if isinstance(query_list, str):
query_list = query_list.split(',')
self.query_list = query_list
if not isinstance(user_id_list, list):
if not os.path.isabs(user_id_list):
user_id_list = os.path.split(
Expand All @@ -67,11 +71,13 @@ def __init__(self, config):
self.user_config_file_path = ''
user_config_list = [{
'user_id': user_id,
'since_date': self.since_date
'since_date': self.since_date,
'query_list': query_list
} for user_id in user_id_list]
self.user_config_list = user_config_list # 要爬取的微博用户的user_config列表
self.user_config = {} # 用户配置,包含用户id和since_date
self.start_date = '' # 获取用户第一条微博时的日期
self.query = ''
self.user = {} # 存储目标微博用户信息
self.got_count = 0 # 存储爬取到的微博数
self.weibo = [] # 存储爬取到的所有微博信息
Expand All @@ -97,6 +103,13 @@ def validate_config(self, config):
logger.warning(u'since_date值应为yyyy-mm-dd形式或整数,请重新输入')
sys.exit()

# 验证query_list
query_list = config.get('query_list') or []
if (not isinstance(query_list, list)) and (not isinstance(
query_list, str)):
logger.warning(u'query_list值应为list类型或字符串,请重新输入')
sys.exit()

# 验证write_mode
write_mode = ['csv', 'json', 'mongo', 'mysql']
if not isinstance(config['write_mode'], list):
Expand Down Expand Up @@ -142,9 +155,12 @@ def get_json(self, params):
def get_weibo_json(self, page):
"""获取网页中微博json数据"""
params = {
'containerid': '107603' + str(self.user_config['user_id']),
'page': page
}
'container_ext': 'profile_uid:' + str(self.user_config['user_id']),
'containerid': '100103type=401&q=' + self.query,
'page_type': 'searchall'
} if self.query else {
'containerid': '107603' + str(self.user_config['user_id'])}
params['page'] = page
js = self.get_json(params)
return js

Expand Down Expand Up @@ -660,6 +676,8 @@ def get_one_page(self, page):
js = self.get_weibo_json(page)
if js['ok']:
weibos = js['data']['cards']
if self.query:
weibos = weibos[0]['card_group']
for w in weibos:
if w['card_type'] == 9:
wb = self.get_one_weibo(w)
Expand All @@ -674,9 +692,9 @@ def get_one_page(self, page):
if self.is_pinned_weibo(w):
continue
else:
logger.info(u'{}已获取{}({})的第{}页微博{}'.format(
logger.info(u'{}已获取{}({})的第{}页{}微博{}'.format(
'-' * 30, self.user['screen_name'],
self.user['id'], page, '-' * 30))
self.user['id'], page, '包含"' + self.query + '"的' if self.query else '', '-' * 30))
return True
if (not self.filter) or (
'retweet' not in wb.keys()):
Expand All @@ -686,6 +704,8 @@ def get_one_page(self, page):
self.print_weibo(wb)
else:
logger.info(u'正在过滤转发微博')
else:
return True
logger.info(u'{}已获取{}({})的第{}页微博{}'.format(
'-' * 30, self.user['screen_name'], self.user['id'], page,
'-' * 30))
Expand Down Expand Up @@ -1087,10 +1107,18 @@ def get_user_config_list(self, file_path):
if len(info) > 0 and info[0].isdigit():
user_config = {}
user_config['user_id'] = info[0]
if len(info) > 2 and self.is_date(info[2]):
user_config['since_date'] = info[2]
if len(info) > 2:
if self.is_date(info[2]):
user_config['since_date'] = info[2]
elif info[2].isdigit():
since_date = date.today() - timedelta(int(info[2]))
user_config['since_date'] = str(since_date)
else:
user_config['since_date'] = self.since_date
if len(info) > 3:
user_config['query_list'] = info[3].split(',')
else:
user_config['query_list'] = self.query_list
if user_config not in user_config_list:
user_config_list.append(user_config)
return user_config_list
Expand All @@ -1107,8 +1135,14 @@ def start(self):
"""运行爬虫"""
try:
for user_config in self.user_config_list:
self.initialize_info(user_config)
self.get_pages()
if len(user_config['query_list']):
for query in user_config['query_list']:
self.query = query
self.initialize_info(user_config)
self.get_pages()
else:
self.initialize_info(user_config)
self.get_pages()
logger.info(u'信息抓取完毕')
logger.info('*' * 100)
if self.user_config_file_path and self.user:
Expand All @@ -1126,7 +1160,7 @@ def get_config():
(os.path.split(os.path.realpath(__file__))[0] + os.sep))
sys.exit()
try:
with open(config_path) as f:
with open(config_path, encoding='utf-8') as f:
config = json.loads(f.read())
return config
except ValueError:
Expand Down

0 comments on commit f4aa77b

Please sign in to comment.