今天实现了一下微信公众号的消息群发功能,整个过程还是比较麻烦的,而且有些坑。记录一下。
在公众平台网站上,为订阅号提供了每天一条的群发权限,为服务号提供每月(自然月)4条的群发权限。而对于某些具备开发能力的公众号运营者,可以通过高级群发接口,实现更灵活的群发能力。
由于群发的次数非常有限。这里是以预览的方式发送给自己。
先看效果
效果
发送成功的返回结果
{"errcode":0,"errmsg":"preview success"}
- 1
这个是预览,群发和预览差不多。
正式环境的效果(2016年9月21日补图)
思路
微信的正确思路
我的具体做法
一些代码
接口地址
// 图文内的图片地址获取接口地址 private String uploadImageUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN"; // 图文封面图片获取接口地址 private String postImageMediaUrl = "http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=image"; // 图文素材上传接口地址 private String postNewsUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token=ACCESS_TOKEN"; // 群发接口地址 private String sendToAllUrl = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=ACCESS_TOKEN"; // 预览接口地址 private String sendToPreviewUrl = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=ACCESS_TOKEN";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
关键代码
@Test public void testSendToAll() { String accessToken = accessTokenUtil.getAccessToken(); // 获取详情的新闻列表 List<NewsArticle> articleList = new ArrayList<>(); // 1.原始的新闻列表 List<NewsArticle> newsList = getArticleList(); if(newsList.size()==0){ logger.info("----------今天没有发文章,任务结束----------"); return; } // 2.封装新闻详情,并得到新闻列表 for (NewsArticle article : newsList) { NewsArticle articleNew = getArticleDetial(article.getUrl()); try { // 下载封面 String localImage = imageService.saveImageToDisk(articleNew.getImagUrl(), imageSavePath); // 上传封面 String jsonStr = HttpUtil.sendPost(postImageMediaUrl.replace("ACCESS_TOKEN", accessToken), new File(localImage)); JSONObject object = new JSONObject(); logger.info("----------上传封面返回结果:{}----------" + jsonStr); try { object = new JSONObject(jsonStr); logger.info("----------得到的图片media_id:{}----------", object.get("media_id")); articleNew.setImageMediaId((String) object.get("media_id")); } catch (Exception e) { logger.error("----------上传封面发生错误:{}", e.getMessage()); } } catch (Exception e) { logger.error("----------下载封面上传封面过程发生错误:{}", e.getMessage()); } articleList.add(articleNew); } logger.info("----------最终图文列表:{}----------", articleList); // 3.群发图文消息封装 List<GroupActicle> articles = new ArrayList<>(); for (NewsArticle NewsArticle : articleList) { GroupActicle article = new GroupActicle(); article.setThumb_media_id(NewsArticle.getImageMediaId()); article.setAuthor(NewsArticle.getAuthor()); article.setTitle(NewsArticle.getTitle()); article.setContent_source_url(NewsArticle.getUrl()); article.setContent(NewsArticle.getContent()); article.setShow_cover_pic(0); articles.add(article); } logger.info("----------图文消息:{}----------", articles); Map<String, Object> jsonMap = new HashMap<>(); jsonMap.put("articles", articles); logger.info("----------最终的图文消息json:{}----------", gson.toJson(jsonMap)); // 4.上传群发图文素材 String postNewsResult = HttpUtil.sendPost(postNewsUrl.replace("ACCESS_TOKEN", accessToken), gson.toJson(jsonMap)); logger.info("----------上传图文素材的返回结果:{}----------", postNewsResult); JSONObject object = new JSONObject(); String mediaId = ""; try { object = new JSONObject(postNewsResult); logger.info("----------得到的media_id:{}----------", object.get("media_id")); mediaId = (String) object.get("media_id"); } catch (Exception e) { logger.error("----------上传图文消息发生错误:{}----------", e.getMessage()); } // 5.选择发送的用户并发送 if (StringUtils.isNotEmpty(mediaId)) { Filter filter = new Filter(true); Mpnews mpnews = new Mpnews(mediaId); SendToAllNews sendAll = new SendToAllNews(); sendAll.setFilter(filter); sendAll.setMpnews(mpnews); sendAll.setMsgtype("mpnews"); // TODO 正式环境用sendToAll SendToOpenIdPreview sendToPreview = new SendToOpenIdPreview("omSsruLEtGPgxjaJTyurGQwQNh8Q"); sendToPreview.setMpnews(mpnews); sendToPreview.setMsgtype("mpnews"); logger.info("----------预览的json:{}----------", gson.toJson(sendToPreview)); // TODO 正式环境换成sendToAllUrl String sentAllResult = HttpUtil.sendPost(sendToPreviewUrl.replace("ACCESS_TOKEN", accessToken), gson.toJson(sendToPreview)); logger.info("----------预览结果:{}----------", sentAllResult); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
注意
微信群发接口图文消息里面包含图片地址,群发之后图文消息内图片无法显示
官方解释
请注意,在图文消息的具体内容中,将过滤外部的图片链接,开发者可以通过下述接口上传图片得到URL,放到图文内容中使用。
上传图文消息内的图片获取URL 请注意,本接口所上传的图片不占用公众号的素材库中图片数量的5000个的限制。图片仅支持jpg/png格式,大小必须在1MB以下。
所以图文消息中图片的src属性必须是腾讯的域名
也就是说必须是以
http://mmbiz.qpic.cn/
开头的,这个需要我们调用如下接口地址
// 图文内的图片上传接口地址 private String uploadImageUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN";
- 1
- 2
坑
如果遇到如下错误
{"errcode":40007,"errmsg":"invalid media_id hint: [51TBDa0350sz63]"}
- 1
上传图片的接口地址为下面的
// 图文封面图片上传接口地址 private String postImageMediaUrl = "http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=image";
- 1
- 2
参考下面的文章
微信上传图文消息invalid media_id hint,thumb_media_id怎么获取
2017年7月23日更新
今天我的群发消息遇到问题了,好久没有整理了,好多人都说群发消息有问题,我今天把我的代码贴出来供大家参考,敏感信息我已经屏蔽,有些实体类也没给出,只是一个思路,仅供参考
package com.jrbac.service.wechat;import java.io.File;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.regex.Matcher;import java.util.regex.Pattern;import org.apache.commons.lang3.StringUtils;import org.json.JSONException;import org.json.JSONObject;import org.jsoup.Jsoup;import org.jsoup.nodes.Document;import org.jsoup.nodes.Element;import org.jsoup.select.Elements;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.google.gson.Gson;import com.google.gson.GsonBuilder;import com.jrbac.dao.MessagePushDao;import com.jrbac.model.wechat.message.GroupSendMessage;import com.jrbac.model.wechat.messsend.CdptsArticle;import com.jrbac.model.wechat.messsend.SendToAllNews;import com.jrbac.model.wechat.messsend.SendToOpenIdPreview;import com.jrbac.util.HttpUtil;import com.jrbac.util.ImageUtil;import com.jrbac.util.PropertiesUtil;import com.jrbac.util.wechat.AccessTokenUtil;@Servicepublic class SendToAllService { // 获取accessToken的 @Autowired private AccessTokenUtil accessTokenUtil; // 读取配置文件的 @Autowired private PropertiesUtil prop; // 将图片下载到本地的 @Autowired private WechatImageDownloadService imageService; // 已群发的消息 @Autowired private MessagePushDao messagePush; private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); // 图文内的图片上传接口地址 private String uploadImageUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN"; // 图文封面图片上传接口地址 private String postImageMediaUrl = "http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=image"; // 图文素材上传接口地址 private String postNewsUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token=ACCESS_TOKEN"; // 群发接口地址 private String sendToAllUrl = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=ACCESS_TOKEN"; // 群发预览接口地址 private String sendToPreviewUrl = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=ACCESS_TOKEN"; // 照片下载路径 private String imageSavePath = "/usr/tmp/images/"; public void doSend() { List<CdptsArticle> toSendNewsList = new ArrayList<>(); // 1.获取新闻列表 toSendNewsList = getArticleListFromCdpts(); // 再去获取一次,有时候第一次获取不到,不知道为什么 if (toSendNewsList.size() == 0) { toSendNewsList = getArticleListFromCdpts(); } // 2.移除已发送的新闻 toSendNewsList = removeAlreadySentArticle(toSendNewsList); if (toSendNewsList.size() == 0) { logger.info("----------没有文章需要群发----------"); return; } // 最多群发8条消息 if (toSendNewsList.size() > 8) { toSendNewsList = toSendNewsList.subList(0, 8); } logger.info("----------群发的消息----------"); for (CdptsArticle article : toSendNewsList) { logger.info("----------{}----------", article.getTitle()); } // 3.上传并设置封面图片 toSendNewsList = setCoverImage(toSendNewsList); // 4.群发图文消息json封装 String groupSendMessageJsonStr = getGroupSendMessageJsonStr(toSendNewsList); // 5.上传群发图文消息 String mediaId = uploadGroupSendMessage(groupSendMessageJsonStr); // 5.群发 if (StringUtils.isNotEmpty(mediaId)) { String mode = prop.get("active"); if (StringUtils.isNotBlank(mode) && mode.equals("dev")) { logger.info("测试环境,预览"); String openId = "你的测试openid"; sendToPreview(toSendNewsList, openId, mediaId); } else { logger.info("正式环境,群发"); sendToAll(toSendNewsList, mediaId); } } } public void sendToPreview(List<CdptsArticle> toSendNewsList, String openId, String mediaId) { String accessToken = accessTokenUtil.getAccessToken(); SendToOpenIdPreview sendToPreview = new SendToOpenIdPreview(openId, mediaId); String sendToPreviewJsonStr = gson.toJson(sendToPreview); logger.info("----------预览的的json:{}----------", sendToPreviewJsonStr); String sentToPreviewResult = HttpUtil.sendPost(sendToPreviewUrl.replace("ACCESS_TOKEN", accessToken), sendToPreviewJsonStr); logger.info("----------预览发送结果:{}----------", sentToPreviewResult); checkSendResult(toSendNewsList, sentToPreviewResult); } public void sendToAll(List<CdptsArticle> toSendNewsList, String mediaId) { String accessToken = accessTokenUtil.getAccessToken(); SendToAllNews sendToAll = new SendToAllNews(mediaId); String sendToAllJsonStr = gson.toJson(sendToAll); logger.info("----------群发的json数据:{}----------", sendToAllJsonStr); String sentToAllResult = HttpUtil.sendPost(sendToAllUrl.replace("ACCESS_TOKEN", accessToken), sendToAllJsonStr); logger.info("----------群发结果:{}----------", sentToAllResult); checkSendResult(toSendNewsList, sentToAllResult); } /** * 查看消息群发结果,并保存群发数据 * * @param toSendNewsList * @param sentToAllResult */ private void checkSendResult(List<CdptsArticle> toSendNewsList, String sentToAllResult) { try { JSONObject object = new JSONObject(sentToAllResult); int errorCode = (Integer) object.get("errcode"); if (errorCode == 0) { for (CdptsArticle articleSend : toSendNewsList) { messagePush.addArticleToPushed(articleSend); } } else { logger.error("---------群发发生了异常{},---------", sentToAllResult); } } catch (JSONException e) { logger.error("---------群发结果json解析异常:{}\n----------jsonStr:{}---------", e.getMessage(), sentToAllResult); } } private String uploadGroupSendMessage(String groupSendMessageJsonStr) { String accessToken = accessTokenUtil.getAccessToken(); String postNewsResult = HttpUtil.sendPost(postNewsUrl.replace("ACCESS_TOKEN", accessToken), groupSendMessageJsonStr); logger.info("----------上传群发图文消息的返回结果:{}----------", postNewsResult); String mediaId = ""; try { JSONObject object = new JSONObject(postNewsResult); mediaId = (String) object.get("media_id"); } catch (JSONException e) { logger.error("上传群发图文消息发生错误{}", postNewsResult); } return mediaId; } private String getGroupSendMessageJsonStr(List<CdptsArticle> toSendNewsList) { List<GroupSendMessage> groupSendMessageList = new ArrayList<>(); for (CdptsArticle cdptsArticle : toSendNewsList) { GroupSendMessage groupSendMessage = new GroupSendMessage(); groupSendMessage.setThumb_media_id(cdptsArticle.getImageMediaId()); groupSendMessage.setAuthor(cdptsArticle.getAuthor()); groupSendMessage.setTitle(cdptsArticle.getTitle()); groupSendMessage.setContent_source_url(cdptsArticle.getUrl()); groupSendMessage.setContent(cdptsArticle.getContent()); groupSendMessage.setShow_cover_pic(0); groupSendMessageList.add(groupSendMessage); } logger.info("----------最终封装的图文消息列表:{}----------", groupSendMessageList); Map<String, Object> jsonMap = new HashMap<>(); jsonMap.put("articles", groupSendMessageList); String groupSendMessageJsonStr = gson.toJson(jsonMap); logger.info("----------最终要发送的图文消息json数据:{}----------", groupSendMessageJsonStr); return groupSendMessageJsonStr; } /** * 设置图文消息的封面图片 * * @param newsList * @return */ private List<CdptsArticle> setCoverImage(List<CdptsArticle> newsList) { String accessToken = accessTokenUtil.getAccessToken(); // 3.将待发送的新闻填充 List<CdptsArticle> detialArticleList = new ArrayList<>(); // 4.封装新闻详情,并得到新闻列表 for (CdptsArticle article : newsList) { CdptsArticle articleWithDetial = getArticleDetialAndReplaceImage(article.getUrl()); try { // 下载封面图片到本地 String localImage = imageService.saveImageToDisk(articleWithDetial.getImageUrl(), imageSavePath); ImageUtil.thumbImage(localImage); // 上传封面到微信 String jsonStr = HttpUtil.sendPost(postImageMediaUrl.replace("ACCESS_TOKEN", accessToken), new File(localImage)); logger.info("----------上传封面图片返回结果:{}----------" + jsonStr); try { JSONObject object = new JSONObject(jsonStr); logger.info("----------得到的封面图片media_id:{}----------", object.get("media_id")); articleWithDetial.setImageMediaId((String) object.get("media_id")); } catch (Exception e) { logger.error("----------上传封面发生错误:{}", e.getMessage()); } } catch (Exception e) { logger.error("----------下载封面,上传封面到微信过程发生错误:{}", e.getMessage()); } detialArticleList.add(articleWithDetial); } logger.info("----------最终群发图文列表:{}----------", detialArticleList); return detialArticleList; } /** * 从新闻列表中删除已发送的文章 * * @return */ public List<CdptsArticle> removeAlreadySentArticle(List<CdptsArticle> articleList) { List<CdptsArticle> messagePushed = messagePush.queryMessagePushed(); // 已发送的 List<String> urlPushedList = new ArrayList<>(); for (CdptsArticle article : messagePushed) { urlPushedList.add(article.getUrl()); } // 删除已发送的 Iterator<CdptsArticle> iterator = articleList.iterator(); while (iterator.hasNext()) { CdptsArticle article = iterator.next(); if (urlPushedList.contains(article.getUrl())) { iterator.remove(); } } return articleList; } /** * 官网获取新闻列表 * * @return */ public List<CdptsArticle> getArticleListFromCdpts() { // 新闻列表地址 String url = prop.get("allNewsHome"); logger.info("----------获取群发消息列表地址:{}----------", url); List<CdptsArticle> articleList = new ArrayList<>(); // 得到页面 String result = HttpUtil.sendGet(url); Document doc = Jsoup.parse(result); String weiSchoolPrefix = prop.get("weischool"); // 内容页面 Elements elements = doc.select("div.m_right"); for (Element element : elements) { Elements contentElements = element.getElementsByTag("ul"); for (Element contents : contentElements) { // System.out.println("内容:"); Elements liContent = contents.getElementsByTag("li"); for (Element content : liContent) { CdptsArticle article = new CdptsArticle(); // System.out.println(content); String time = content.getElementsByTag("span").first().text(); // System.out.println(content.getElementsByTag("span").first().text()); article.setTime(time); String title = content.getElementsByTag("a").first().text(); article.setTitle(title); String originalUrl = content.select("a[href]").attr("href"); article.setUrl(weiSchoolPrefix + originalUrl); articleList.add(article); } } } logger.info("----------获取到的新闻列表:{}----------", articleList); return articleList; } // 图片校验 private static boolean isImage(String imageSrc) { String reg = ".+(.JPEG|.jpeg|.JPG|.jpg|.GIF|.gif|.BMP|.bmp|.PNG|.png)$"; Pattern pattern = Pattern.compile(reg); Matcher matcher = pattern.matcher(imageSrc.toLowerCase()); return (matcher.find()); } /** * 获取新闻详情并替换图片为微信的 * * @param articleUrl * @return */ private CdptsArticle getArticleDetialAndReplaceImage(String articleUrl) { String accessToken = accessTokenUtil.getAccessToken(); CdptsArticle article = new CdptsArticle(); article.setUrl(articleUrl); // 具体页面 String remoteUrl = prop.get("remoteDomain") + "/Article/Index/" + articleUrl.split("/Article/Index/")[1]; logger.info("----------获取页面详情url:{}----------", remoteUrl); // 得到页面 String result = HttpUtil.sendGet(remoteUrl); Document doc = Jsoup.parse(result); // 内容页面 Elements elements = doc.select("div.m_right"); for (Element element : elements) { // System.out.println("原始内容:\n" + element); // 作者信息 Element pageInfo = element.select("p.D_infor").remove(0); // 标题 Element titleInfo = element.select("h1.D_title").remove(0); article.setTitle(titleInfo.text()); String page = pageInfo.toString(); // System.out.println("作者相关信息:\n" + page); // 所在板块 String module = element.getElementsByTag("h2").first().select("a[href]").text(); // 页面内容 String content = element.select("div.r_cont").html(); // 替换所有的图片路径 Elements images = element.select("div.r_cont").select("img"); for (Element image : images) { String imageUrl = image.attr("src"); if(!isImage(imageUrl)){// 不是图片格式,直接删掉 // image.attr("src", prop.get("localhost") + "/assets/image/school/bg.jpg"); image.remove(); continue; }else{ if (imageUrl.startsWith("http://")) { continue; } else if (imageUrl.startsWith("/Upload")) { image.attr("src", prop.get("remoteDomain") + image.attr("src")); } else { image.attr("src", prop.get("localhost") + "/assets/image/school/bg.jpg"); } } // if (!image.attr("src").startsWith("http://")) { // image.attr("src", prop.get("remoteDomain") + // image.attr("src")); // } } content = element.select("div.r_cont").html(); // 作者相关 // 作者相关 Elements info = element.getElementsByTag("p").first().getElementsByTag("span"); article.setTime(info.get(0).text().replace("发表时间: ", "")); article.setAuthor(info.get(1).text().replace("发布人:", "")); article.setCount(info.get(2).text().replace("点击率:", "")); article.setModule(module); // System.out.println("[所在板块]\t" + module); String contents = content.replace(page, "").replace(titleInfo.toString(), ""); // System.out.println("[页面内容]\t" + contents); Document docImage = Jsoup.parse(contents); Elements elementImage = docImage.select("img"); if (elementImage.size() == 0) { article.setImageUrl(prop.get("localhost") + "/assets/image/school/bg.jpg"); } else { article.setImageUrl(elementImage.get(0).attr("src")); } for (Element imageElement : elementImage) { try { // 下载文件 logger.info("----------下载页面中的图片src:{}----------", imageElement.attr("src")); String imageRealPath = imageService.saveImageToDisk(imageElement.attr("src"), imageSavePath); logger.info("----------图片保存路径:{}----------", imageRealPath); // 压缩处理 ImageUtil.thumbImage(imageRealPath); // 上传到微信 logger.info("----------上传到微信----------"); String responseStr = HttpUtil.sendPost(uploadImageUrl.replace("ACCESS_TOKEN", accessToken), new File(imageRealPath)); logger.info("----------上传结果:{}----------", responseStr); JSONObject object = new JSONObject(); try { object = new JSONObject(responseStr); logger.info("----------得到上传图片的url:{}----------", object.get("url")); imageElement.attr("src", (String) object.get("url")); } catch (Exception e) { logger.error("----------上传图片到微信发生了错误:{}" + responseStr); } } catch (Exception e) { logger.error("----------下载图片并上传到微信过程发生了错误:{}" + e.getMessage()); } } String contentFinal = docImage.select("div.D_cont").html(); article.setContent(contentFinal); } logger.info("----------获取页面详情返回结果:{}----------", article); return article; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400