Quando você recebe uma resposta, o Requests faz uma suposição sobre a codificação a ser usada para decodificar a resposta quando você acessa o atributo Response.text. Requests primeiro verifica uma codificação no cabeçalho HTTP e, se nenhuma estiver presente, usa o chardet para tentar adivinhar a codificação.
Mas a única vez que Requests não fará isso é se nenhum charset explícito estiver presente nos HTTP headers e o Content-Type header contiver texto. Nessa situação, o RFC 2616 especifica que o conjunto de caracteres padrão deve ser encoding ISO-8859-1. Requests segue a especificação neste caso. Se você precisar de uma codificação diferente, poderá definir manualmente a propriedade Response.encoding ou usar o Response.content bruto.
Que é o caso desse site da Câmara de São Paulo, ele não está formatado com excelência - muitos sites públicos devem ter esse problema. Ao inspecionar os request headers mostra que, de fato, nenhum charset explícito está presente nos HTTP headers e o Content-Type header contém texto.
req.headers['content-type']
'text/html'
Portanto, o requests segue fielmente o padrão e são decodificadas como ISO-8859-1 (latin-1).
No conteúdo da resposta, um conjunto de caracteres é especificado:
<META http-equiv="Content-Type" content="text/html; charset=utf-16">
No entanto, isso está errado: a decodificação como UTF-16 produz mojibake.
chardet identifica corretamente a codificação como UTF-8.
Então, para resumir:
+ Não há uma maneira geral de determinar a codificação de texto com precisão total
+ Nesse caso específico, a codificação correta é UTF-8.
Código final para resolver:
req.encoding = 'UTF-8'
soup = BeautifulSoup(req.text,'lxml')
soup.find('h1').text
'\r\n CÂMARA MUNICIPAL DE SÃO PAULO'
(Com a ajuda de snakecharmerb )